xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision e7bcf475904424ac375d408f4feb0a717787c2a4)
177dd8813SKowalski, Kamil /*
26be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
36be832e2SEd Tanous 
46be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
56be832e2SEd Tanous you may not use this file except in compliance with the License.
66be832e2SEd Tanous You may obtain a copy of the License at
76be832e2SEd Tanous 
86be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
96be832e2SEd Tanous 
106be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
116be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
126be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136be832e2SEd Tanous See the License for the specific language governing permissions and
146be832e2SEd Tanous limitations under the License.
1577dd8813SKowalski, Kamil */
1677dd8813SKowalski, Kamil #pragma once
179712f8acSEd Tanous 
18d5c80ad9SNan Zhou #include "error_messages.hpp"
198a7c4b47SNan Zhou #include "http_connection.hpp"
20d5c80ad9SNan Zhou #include "http_request.hpp"
21d5c80ad9SNan Zhou #include "http_response.hpp"
22185444b1SNan Zhou #include "human_sort.hpp"
23d5c80ad9SNan Zhou #include "logging.hpp"
24faf100f9SEd Tanous 
25*e7bcf475SJayanth Othayoth #include <boost/system/result.hpp>
26*e7bcf475SJayanth Othayoth #include <boost/url/parse.hpp>
27*e7bcf475SJayanth Othayoth #include <boost/url/url_view.hpp>
28faf100f9SEd Tanous #include <nlohmann/json.hpp>
290627a2c7SEd Tanous 
30185444b1SNan Zhou #include <algorithm>
31d5c80ad9SNan Zhou #include <array>
32d5c80ad9SNan Zhou #include <cmath>
33d5c80ad9SNan Zhou #include <cstddef>
34d5c80ad9SNan Zhou #include <cstdint>
35d5c80ad9SNan Zhou #include <limits>
36d5c80ad9SNan Zhou #include <map>
37d5c80ad9SNan Zhou #include <optional>
383544d2a7SEd Tanous #include <ranges>
39ea2e6eecSWilly Tu #include <span>
40d5c80ad9SNan Zhou #include <string>
41d5c80ad9SNan Zhou #include <string_view>
42d5c80ad9SNan Zhou #include <type_traits>
43d5c80ad9SNan Zhou #include <utility>
44d5c80ad9SNan Zhou #include <variant>
45d5c80ad9SNan Zhou #include <vector>
46d5c80ad9SNan Zhou 
471e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request
481214b7e7SGunnar Mills 
491abe55efSEd Tanous namespace redfish
501abe55efSEd Tanous {
511abe55efSEd Tanous 
521abe55efSEd Tanous namespace json_util
531abe55efSEd Tanous {
5477dd8813SKowalski, Kamil 
5577dd8813SKowalski, Kamil /**
5677dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
5777dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
5877dd8813SKowalski, Kamil  *
5977dd8813SKowalski, Kamil  * @param[io]  res       Response object
6077dd8813SKowalski, Kamil  * @param[in]  req       Request object
6177dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
6277dd8813SKowalski, Kamil  *
6377dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
6477dd8813SKowalski, Kamil  *         been filled with message and ended.
6577dd8813SKowalski, Kamil  */
6655c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
6777dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
689712f8acSEd Tanous namespace details
699712f8acSEd Tanous {
70771cfa0fSJason M. Bills 
711214b7e7SGunnar Mills template <typename Type>
722c70f800SEd Tanous struct IsOptional : std::false_type
731214b7e7SGunnar Mills {};
749712f8acSEd Tanous 
75771cfa0fSJason M. Bills template <typename Type>
762c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
771214b7e7SGunnar Mills {};
789712f8acSEd Tanous 
79771cfa0fSJason M. Bills template <typename Type>
802c70f800SEd Tanous struct IsVector : std::false_type
811214b7e7SGunnar Mills {};
82b1556427SEd Tanous 
831214b7e7SGunnar Mills template <typename Type>
842c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
851214b7e7SGunnar Mills {};
86b1556427SEd Tanous 
871214b7e7SGunnar Mills template <typename Type>
882c70f800SEd Tanous struct IsStdArray : std::false_type
891214b7e7SGunnar Mills {};
90318226c2SJames Feist 
91318226c2SJames Feist template <typename Type, std::size_t size>
922c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
931214b7e7SGunnar Mills {};
94318226c2SJames Feist 
958099c517SEd Tanous template <typename Type>
968099c517SEd Tanous struct IsVariant : std::false_type
978099c517SEd Tanous {};
988099c517SEd Tanous 
998099c517SEd Tanous template <typename... Types>
1008099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type
1018099c517SEd Tanous {};
1028099c517SEd Tanous 
103471a5eb8SAppaRao Puli enum class UnpackErrorCode
104471a5eb8SAppaRao Puli {
105471a5eb8SAppaRao Puli     success,
106471a5eb8SAppaRao Puli     invalidType,
107471a5eb8SAppaRao Puli     outOfRange
108471a5eb8SAppaRao Puli };
109471a5eb8SAppaRao Puli 
110a6acbb31SJames Feist template <typename ToType, typename FromType>
111c09966bdSEd Tanous bool checkRange(const FromType& from [[maybe_unused]],
112c09966bdSEd Tanous                 std::string_view key [[maybe_unused]])
113a6acbb31SJames Feist {
114a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
115a6acbb31SJames Feist     {
116ee344e0fSEd Tanous         if (std::isnan(from))
117a6acbb31SJames Feist         {
11862598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
119a6acbb31SJames Feist             return false;
120a6acbb31SJames Feist         }
121a6acbb31SJames Feist     }
122c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::max() <
123c09966bdSEd Tanous                   std::numeric_limits<FromType>::max())
124c09966bdSEd Tanous     {
125c09966bdSEd Tanous         if (from > std::numeric_limits<ToType>::max())
126c09966bdSEd Tanous         {
127c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
128c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
129c09966bdSEd Tanous             return false;
130c09966bdSEd Tanous         }
131c09966bdSEd Tanous     }
132c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::lowest() >
133c09966bdSEd Tanous                   std::numeric_limits<FromType>::lowest())
134c09966bdSEd Tanous     {
135c09966bdSEd Tanous         if (from < std::numeric_limits<ToType>::lowest())
136c09966bdSEd Tanous         {
137c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
138c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
139c09966bdSEd Tanous             return false;
140c09966bdSEd Tanous         }
141c09966bdSEd Tanous     }
142a6acbb31SJames Feist 
143a6acbb31SJames Feist     return true;
144a6acbb31SJames Feist }
145a6acbb31SJames Feist 
146771cfa0fSJason M. Bills template <typename Type>
147471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1488099c517SEd Tanous                                          std::string_view key, Type& value);
1498099c517SEd Tanous 
1508099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1518099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1528099c517SEd Tanous                                    std::variant<Args...>& v)
1538099c517SEd Tanous {
1548099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1558099c517SEd Tanous     {
156ed4de7a8SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type{};
1578099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1588099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1598099c517SEd Tanous         {
1608099c517SEd Tanous             v = std::move(type);
1618099c517SEd Tanous             return unpack;
1628099c517SEd Tanous         }
1638099c517SEd Tanous 
1648099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1658099c517SEd Tanous     }
1668099c517SEd Tanous     return UnpackErrorCode::invalidType;
1678099c517SEd Tanous }
1688099c517SEd Tanous 
1698099c517SEd Tanous template <typename Type>
1708099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
171ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
172771cfa0fSJason M. Bills {
173471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
17441352c24SSantosh Puranik 
175a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
176771cfa0fSJason M. Bills     {
177a6acbb31SJames Feist         double helper = 0;
178a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
179771cfa0fSJason M. Bills 
180771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
181771cfa0fSJason M. Bills         {
182a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
183a6acbb31SJames Feist             if (intPtr != nullptr)
184771cfa0fSJason M. Bills             {
185a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
186a6acbb31SJames Feist                 jsonPtr = &helper;
187771cfa0fSJason M. Bills             }
188a6acbb31SJames Feist         }
1895eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1905eb2bef2SAppaRao Puli         {
191471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1925eb2bef2SAppaRao Puli         }
193cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
194771cfa0fSJason M. Bills         {
195471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
196771cfa0fSJason M. Bills         }
197771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
198771cfa0fSJason M. Bills     }
199a6acbb31SJames Feist 
200a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
201a6acbb31SJames Feist     {
202a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
203271584abSEd Tanous         if (jsonPtr == nullptr)
204271584abSEd Tanous         {
205471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
206271584abSEd Tanous         }
207cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
208a6acbb31SJames Feist         {
209471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
210a6acbb31SJames Feist         }
211a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
212a6acbb31SJames Feist     }
213a6acbb31SJames Feist 
214bd79bce8SPatrick Williams     else if constexpr ((std::is_unsigned_v<Type>) &&
215bd79bce8SPatrick Williams                        (!std::is_same_v<bool, Type>))
216a6acbb31SJames Feist     {
217a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
218271584abSEd Tanous         if (jsonPtr == nullptr)
219271584abSEd Tanous         {
220471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
221271584abSEd Tanous         }
222cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
223a6acbb31SJames Feist         {
224471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
225a6acbb31SJames Feist         }
226a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
227a6acbb31SJames Feist     }
228a6acbb31SJames Feist 
2290627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2300627a2c7SEd Tanous     {
2310627a2c7SEd Tanous         value = std::move(jsonValue);
2320627a2c7SEd Tanous     }
2338099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2348099c517SEd Tanous     {
2358099c517SEd Tanous         if (!jsonValue.is_null())
2368099c517SEd Tanous         {
2378099c517SEd Tanous             return UnpackErrorCode::invalidType;
2388099c517SEd Tanous         }
2398099c517SEd Tanous     }
240ed4de7a8SEd Tanous     else if constexpr (IsVector<Type>::value)
241ed4de7a8SEd Tanous     {
242ed4de7a8SEd Tanous         nlohmann::json::object_t* obj =
243ed4de7a8SEd Tanous             jsonValue.get_ptr<nlohmann::json::object_t*>();
244ed4de7a8SEd Tanous         if (obj == nullptr)
245ed4de7a8SEd Tanous         {
246ed4de7a8SEd Tanous             return UnpackErrorCode::invalidType;
247ed4de7a8SEd Tanous         }
248ed4de7a8SEd Tanous 
249ed4de7a8SEd Tanous         for (const auto& val : *obj)
250ed4de7a8SEd Tanous         {
251ed4de7a8SEd Tanous             value.emplace_back();
252ed4de7a8SEd Tanous             ret = unpackValueWithErrorCode<typename Type::value_type>(
253ed4de7a8SEd Tanous                       val, key, value.back()) &&
254ed4de7a8SEd Tanous                   ret;
255ed4de7a8SEd Tanous         }
256ed4de7a8SEd Tanous     }
257471a5eb8SAppaRao Puli     else
258471a5eb8SAppaRao Puli     {
259471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
260471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
261471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
262471a5eb8SAppaRao Puli         {
26362598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
26462598e31SEd Tanous                              jsonValue.type_name());
265471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
266471a5eb8SAppaRao Puli         }
267471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
268471a5eb8SAppaRao Puli     }
269471a5eb8SAppaRao Puli     return ret;
270471a5eb8SAppaRao Puli }
271471a5eb8SAppaRao Puli 
272471a5eb8SAppaRao Puli template <typename Type>
273ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
274471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
275471a5eb8SAppaRao Puli {
276471a5eb8SAppaRao Puli     bool ret = true;
277471a5eb8SAppaRao Puli 
2782c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
279471a5eb8SAppaRao Puli     {
280471a5eb8SAppaRao Puli         value.emplace();
281471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
282471a5eb8SAppaRao Puli                                                      *value) &&
283471a5eb8SAppaRao Puli               ret;
284471a5eb8SAppaRao Puli     }
2852c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
286318226c2SJames Feist     {
2870bdda665SEd Tanous         nlohmann::json::array_t* arr =
2880bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2890bdda665SEd Tanous         if (arr == nullptr)
290318226c2SJames Feist         {
2912e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
29241352c24SSantosh Puranik             return false;
293318226c2SJames Feist         }
294318226c2SJames Feist         if (jsonValue.size() != value.size())
295318226c2SJames Feist         {
2962e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
29741352c24SSantosh Puranik             return false;
298318226c2SJames Feist         }
299318226c2SJames Feist         size_t index = 0;
3000bdda665SEd Tanous         for (auto& val : *arr)
301318226c2SJames Feist         {
3020bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
30341352c24SSantosh Puranik                                                          value[index++]) &&
30441352c24SSantosh Puranik                   ret;
305318226c2SJames Feist         }
306318226c2SJames Feist     }
3072c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
308b1556427SEd Tanous     {
3090bdda665SEd Tanous         nlohmann::json::array_t* arr =
3100bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3110bdda665SEd Tanous         if (arr == nullptr)
312b1556427SEd Tanous         {
3132e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
31441352c24SSantosh Puranik             return false;
315b1556427SEd Tanous         }
316b1556427SEd Tanous 
3170bdda665SEd Tanous         for (auto& val : *arr)
318b1556427SEd Tanous         {
319b1556427SEd Tanous             value.emplace_back();
3200bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
32141352c24SSantosh Puranik                                                          value.back()) &&
32241352c24SSantosh Puranik                   ret;
323b1556427SEd Tanous         }
324b1556427SEd Tanous     }
3258099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3268099c517SEd Tanous     {
3278099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3288099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3298099c517SEd Tanous         {
3308099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3318099c517SEd Tanous             {
3328099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3338099c517SEd Tanous             }
3348099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3358099c517SEd Tanous             {
336340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
3378099c517SEd Tanous             }
3388099c517SEd Tanous             return false;
3398099c517SEd Tanous         }
3408099c517SEd Tanous     }
341771cfa0fSJason M. Bills     else
342771cfa0fSJason M. Bills     {
343471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
344471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
345771cfa0fSJason M. Bills         {
346471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
347471a5eb8SAppaRao Puli             {
3482e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
349471a5eb8SAppaRao Puli             }
350471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
351471a5eb8SAppaRao Puli             {
352340d74c8SMyung Bae                 messages::propertyValueOutOfRange(res, jsonValue, key);
353471a5eb8SAppaRao Puli             }
35441352c24SSantosh Puranik             return false;
355771cfa0fSJason M. Bills         }
356771cfa0fSJason M. Bills     }
357471a5eb8SAppaRao Puli 
358471a5eb8SAppaRao Puli     return ret;
359471a5eb8SAppaRao Puli }
360471a5eb8SAppaRao Puli 
361471a5eb8SAppaRao Puli template <typename Type>
362ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
363471a5eb8SAppaRao Puli {
364471a5eb8SAppaRao Puli     bool ret = true;
3652c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
366471a5eb8SAppaRao Puli     {
367471a5eb8SAppaRao Puli         value.emplace();
368471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
369471a5eb8SAppaRao Puli               ret;
370471a5eb8SAppaRao Puli     }
3712c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
372471a5eb8SAppaRao Puli     {
3730bdda665SEd Tanous         nlohmann::json::array_t* arr =
3740bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3750bdda665SEd Tanous         if (arr == nullptr)
376471a5eb8SAppaRao Puli         {
377471a5eb8SAppaRao Puli             return false;
378471a5eb8SAppaRao Puli         }
379471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
380471a5eb8SAppaRao Puli         {
381471a5eb8SAppaRao Puli             return false;
382471a5eb8SAppaRao Puli         }
383471a5eb8SAppaRao Puli         size_t index = 0;
3840bdda665SEd Tanous         for (const auto& val : *arr)
385471a5eb8SAppaRao Puli         {
3860bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
387471a5eb8SAppaRao Puli                                                          value[index++]) &&
388471a5eb8SAppaRao Puli                   ret;
389471a5eb8SAppaRao Puli         }
390471a5eb8SAppaRao Puli     }
3912c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
392471a5eb8SAppaRao Puli     {
3930bdda665SEd Tanous         nlohmann::json::array_t* arr =
3940bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3950bdda665SEd Tanous         if (arr == nullptr)
396471a5eb8SAppaRao Puli         {
397471a5eb8SAppaRao Puli             return false;
398471a5eb8SAppaRao Puli         }
399471a5eb8SAppaRao Puli 
4000bdda665SEd Tanous         for (const auto& val : *arr)
401471a5eb8SAppaRao Puli         {
402471a5eb8SAppaRao Puli             value.emplace_back();
4030bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
404471a5eb8SAppaRao Puli                                                          value.back()) &&
405471a5eb8SAppaRao Puli                   ret;
406471a5eb8SAppaRao Puli         }
407471a5eb8SAppaRao Puli     }
408471a5eb8SAppaRao Puli     else
409471a5eb8SAppaRao Puli     {
410471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
411471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
412471a5eb8SAppaRao Puli         {
413471a5eb8SAppaRao Puli             return false;
414471a5eb8SAppaRao Puli         }
415471a5eb8SAppaRao Puli     }
416471a5eb8SAppaRao Puli 
41741352c24SSantosh Puranik     return ret;
418771cfa0fSJason M. Bills }
4199712f8acSEd Tanous } // namespace details
4209712f8acSEd Tanous 
421ea2e6eecSWilly Tu // clang-format off
422ea2e6eecSWilly Tu using UnpackVariant = std::variant<
423ea2e6eecSWilly Tu     uint8_t*,
424ea2e6eecSWilly Tu     uint16_t*,
425ea2e6eecSWilly Tu     int16_t*,
426ea2e6eecSWilly Tu     uint32_t*,
427ea2e6eecSWilly Tu     int32_t*,
428ea2e6eecSWilly Tu     uint64_t*,
429ea2e6eecSWilly Tu     int64_t*,
430ea2e6eecSWilly Tu     bool*,
431ea2e6eecSWilly Tu     double*,
432ea2e6eecSWilly Tu     std::string*,
433b6164cbeSEd Tanous     nlohmann::json::object_t*,
4348099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4358099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4368099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4378099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4388099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4398099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4408099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4418099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4428099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4438099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
444ea2e6eecSWilly Tu     std::vector<uint8_t>*,
445ea2e6eecSWilly Tu     std::vector<uint16_t>*,
446ea2e6eecSWilly Tu     std::vector<int16_t>*,
447ea2e6eecSWilly Tu     std::vector<uint32_t>*,
448ea2e6eecSWilly Tu     std::vector<int32_t>*,
449ea2e6eecSWilly Tu     std::vector<uint64_t>*,
450ea2e6eecSWilly Tu     std::vector<int64_t>*,
451ea2e6eecSWilly Tu     //std::vector<bool>*,
452ea2e6eecSWilly Tu     std::vector<double>*,
453ea2e6eecSWilly Tu     std::vector<std::string>*,
454b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
455ea2e6eecSWilly Tu     std::optional<uint8_t>*,
456ea2e6eecSWilly Tu     std::optional<uint16_t>*,
457ea2e6eecSWilly Tu     std::optional<int16_t>*,
458ea2e6eecSWilly Tu     std::optional<uint32_t>*,
459ea2e6eecSWilly Tu     std::optional<int32_t>*,
460ea2e6eecSWilly Tu     std::optional<uint64_t>*,
461ea2e6eecSWilly Tu     std::optional<int64_t>*,
462ea2e6eecSWilly Tu     std::optional<bool>*,
463ea2e6eecSWilly Tu     std::optional<double>*,
464ea2e6eecSWilly Tu     std::optional<std::string>*,
465b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
466ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
467ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
468ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
469ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
470ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
471ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
472ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
473ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
474ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
475ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
4768099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4778099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4788099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4798099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4808099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4818099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4828099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4838099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
4848099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
4858099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
4868099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
4878099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
488ed4de7a8SEd Tanous     std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*,
489ed4de7a8SEd Tanous 
490ed4de7a8SEd Tanous     // Note, these types are kept for historical completeness, but should not be used,
491ed4de7a8SEd Tanous     // As they do not provide object type safety.  Instead, rely on nlohmann::json::object_t
492ed4de7a8SEd Tanous     // Will be removed Q2 2025
493ed4de7a8SEd Tanous     nlohmann::json*,
494ed4de7a8SEd Tanous     std::optional<std::vector<nlohmann::json>>*,
495ed4de7a8SEd Tanous     std::vector<nlohmann::json>*,
496ed4de7a8SEd Tanous     std::optional<nlohmann::json>*
497ea2e6eecSWilly Tu >;
498ea2e6eecSWilly Tu // clang-format on
499ea2e6eecSWilly Tu 
500ea2e6eecSWilly Tu struct PerUnpack
501ea2e6eecSWilly Tu {
502ea2e6eecSWilly Tu     std::string_view key;
503ea2e6eecSWilly Tu     UnpackVariant value;
504ea2e6eecSWilly Tu     bool complete = false;
505ea2e6eecSWilly Tu };
506ea2e6eecSWilly Tu 
507ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
508b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack);
509b6164cbeSEd Tanous 
510b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
511b6164cbeSEd Tanous                                  crow::Response& res,
512ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
5139712f8acSEd Tanous {
51441352c24SSantosh Puranik     bool result = true;
515b6164cbeSEd Tanous     for (auto& item : obj)
5169712f8acSEd Tanous     {
517ea2e6eecSWilly Tu         size_t unpackIndex = 0;
518ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
519ea2e6eecSWilly Tu         {
520ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
521ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
522ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
523ea2e6eecSWilly Tu             std::string_view leftover;
524ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
525ea2e6eecSWilly Tu             {
526ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
527ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
528ea2e6eecSWilly Tu             }
529ea2e6eecSWilly Tu 
530d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
531ea2e6eecSWilly Tu             {
532ea2e6eecSWilly Tu                 continue;
533ea2e6eecSWilly Tu             }
534ea2e6eecSWilly Tu 
535ea2e6eecSWilly Tu             // Sublevel key
536ea2e6eecSWilly Tu             if (!leftover.empty())
537ea2e6eecSWilly Tu             {
538ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
539ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
540ea2e6eecSWilly Tu                 nlohmann::json j;
541d91415c4SEd Tanous                 result = details::unpackValue<nlohmann::json>(item.second, key,
542ea2e6eecSWilly Tu                                                               res, j) &&
54341352c24SSantosh Puranik                          result;
54455f79e6fSEd Tanous                 if (!result)
545ea2e6eecSWilly Tu                 {
546ea2e6eecSWilly Tu                     return result;
5479712f8acSEd Tanous                 }
5489712f8acSEd Tanous 
549ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
550ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
551ea2e6eecSWilly Tu                 {
552ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
553ea2e6eecSWilly Tu                     {
554ea2e6eecSWilly Tu                         continue;
555ea2e6eecSWilly Tu                     }
556ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
557ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
558ea2e6eecSWilly Tu                     p.complete = true;
5599712f8acSEd Tanous                 }
56077dd8813SKowalski, Kamil 
561ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
562ea2e6eecSWilly Tu                 break;
563ea2e6eecSWilly Tu             }
564ea2e6eecSWilly Tu 
565bd79bce8SPatrick Williams             result =
566bd79bce8SPatrick Williams                 std::visit(
5675ea927bbSEd Tanous                     [&item, &unpackSpec, &res](auto& val) {
568ea2e6eecSWilly Tu                         using ContainedT =
569ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
570ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
571d91415c4SEd Tanous                             item.second, unpackSpec.key, res, *val);
572ea2e6eecSWilly Tu                     },
573ea2e6eecSWilly Tu                     unpackSpec.value) &&
574ea2e6eecSWilly Tu                 result;
575ea2e6eecSWilly Tu 
576ea2e6eecSWilly Tu             unpackSpec.complete = true;
577ea2e6eecSWilly Tu             break;
578ea2e6eecSWilly Tu         }
579ea2e6eecSWilly Tu 
580ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
581ea2e6eecSWilly Tu         {
582d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
583ea2e6eecSWilly Tu             result = false;
584ea2e6eecSWilly Tu         }
585ea2e6eecSWilly Tu     }
586ea2e6eecSWilly Tu 
587ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
588ea2e6eecSWilly Tu     {
58955f79e6fSEd Tanous         if (!perUnpack.complete)
590ea2e6eecSWilly Tu         {
591ea2e6eecSWilly Tu             bool isOptional = std::visit(
5925ea927bbSEd Tanous                 [](auto& val) {
593ea2e6eecSWilly Tu                     using ContainedType =
594ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
595ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
596ea2e6eecSWilly Tu                 },
597ea2e6eecSWilly Tu                 perUnpack.value);
598ea2e6eecSWilly Tu             if (isOptional)
599ea2e6eecSWilly Tu             {
600ea2e6eecSWilly Tu                 continue;
601ea2e6eecSWilly Tu             }
602ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
603ea2e6eecSWilly Tu             result = false;
604ea2e6eecSWilly Tu         }
605ea2e6eecSWilly Tu     }
606ea2e6eecSWilly Tu     return result;
607ea2e6eecSWilly Tu }
608ea2e6eecSWilly Tu 
609b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
610b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack)
611b6164cbeSEd Tanous {
612b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
613b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
614b6164cbeSEd Tanous     if (obj == nullptr)
615b6164cbeSEd Tanous     {
616b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
617b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
618b6164cbeSEd Tanous         return false;
619b6164cbeSEd Tanous     }
620b6164cbeSEd Tanous     return readJsonHelperObject(*obj, res, toUnpack);
621b6164cbeSEd Tanous }
622b6164cbeSEd Tanous 
62389492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
624ea2e6eecSWilly Tu 
625ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
626ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
6275ea927bbSEd Tanous                  FirstType&& first, UnpackTypes&&... in)
628ea2e6eecSWilly Tu {
629ea2e6eecSWilly Tu     if (toPack.empty())
630ea2e6eecSWilly Tu     {
631ea2e6eecSWilly Tu         return;
632ea2e6eecSWilly Tu     }
633ea2e6eecSWilly Tu     toPack[0].key = key;
634ea2e6eecSWilly Tu     toPack[0].value = &first;
635ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
636ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
637ea2e6eecSWilly Tu }
638ea2e6eecSWilly Tu 
639ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
640b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
641b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
642b6164cbeSEd Tanous                     UnpackTypes&&... in)
643ea2e6eecSWilly Tu {
644ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
645ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
6465ea927bbSEd Tanous     packVariant(toUnpack2, key, std::forward<FirstType>(first),
6475ea927bbSEd Tanous                 std::forward<UnpackTypes&&>(in)...);
648b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
649ea2e6eecSWilly Tu }
650ea2e6eecSWilly Tu 
651b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
652b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
653b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
654b6164cbeSEd Tanous {
655b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
656b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
657b6164cbeSEd Tanous     if (obj == nullptr)
658b6164cbeSEd Tanous     {
659b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
660b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
661b6164cbeSEd Tanous         return false;
662b6164cbeSEd Tanous     }
6635be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6645be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
665b6164cbeSEd Tanous }
666b6164cbeSEd Tanous 
667b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t>
668ea2e6eecSWilly Tu     readJsonPatchHelper(const crow::Request& req, crow::Response& res)
6690627a2c7SEd Tanous {
6700627a2c7SEd Tanous     nlohmann::json jsonRequest;
6710627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6720627a2c7SEd Tanous     {
67362598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
674ea2e6eecSWilly Tu         return std::nullopt;
6750627a2c7SEd Tanous     }
676357bb8f8SEd Tanous     nlohmann::json::object_t* object =
677357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
678357bb8f8SEd Tanous     if (object == nullptr || object->empty())
67915ed6780SWilly Tu     {
68062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
68115ed6780SWilly Tu         messages::emptyJSON(res);
682ea2e6eecSWilly Tu         return std::nullopt;
683ea2e6eecSWilly Tu     }
684357bb8f8SEd Tanous     std::erase_if(*object,
685357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
686357bb8f8SEd Tanous                       return item.first.starts_with("@odata.");
687357bb8f8SEd Tanous                   });
688357bb8f8SEd Tanous     if (object->empty())
689357bb8f8SEd Tanous     {
690357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
691357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
692357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
693357bb8f8SEd Tanous         messages::noOperation(res);
694357bb8f8SEd Tanous         return std::nullopt;
695357bb8f8SEd Tanous     }
696357bb8f8SEd Tanous 
697b6164cbeSEd Tanous     return {std::move(*object)};
698ea2e6eecSWilly Tu }
699ea2e6eecSWilly Tu 
700ea2e6eecSWilly Tu template <typename... UnpackTypes>
701ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
702ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
703ea2e6eecSWilly Tu {
70476b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
70576b038f2SEd Tanous         readJsonPatchHelper(req, res);
706e01d0c36SEd Tanous     if (!jsonRequest)
707ea2e6eecSWilly Tu     {
70815ed6780SWilly Tu         return false;
70915ed6780SWilly Tu     }
71076b038f2SEd Tanous     if (jsonRequest->empty())
711b6164cbeSEd Tanous     {
712b6164cbeSEd Tanous         messages::emptyJSON(res);
713b6164cbeSEd Tanous         return false;
714b6164cbeSEd Tanous     }
71515ed6780SWilly Tu 
71676b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
717b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
71815ed6780SWilly Tu }
71915ed6780SWilly Tu 
72015ed6780SWilly Tu template <typename... UnpackTypes>
72115ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
722ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
72315ed6780SWilly Tu {
72415ed6780SWilly Tu     nlohmann::json jsonRequest;
72515ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
72615ed6780SWilly Tu     {
72762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
72815ed6780SWilly Tu         return false;
72915ed6780SWilly Tu     }
730b6164cbeSEd Tanous     nlohmann::json::object_t* object =
731b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
732b6164cbeSEd Tanous     if (object == nullptr)
733b6164cbeSEd Tanous     {
734b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
735b6164cbeSEd Tanous         messages::emptyJSON(res);
736b6164cbeSEd Tanous         return false;
737b6164cbeSEd Tanous     }
738b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
739b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7400627a2c7SEd Tanous }
741185444b1SNan Zhou 
742185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
743185444b1SNan Zhou // @odata.id key
7444e196b9aSEd Tanous inline int objectKeyCmp(std::string_view key, const nlohmann::json& a,
7454e196b9aSEd Tanous                         const nlohmann::json& b)
746185444b1SNan Zhou {
747185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
748185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
749185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
750185444b1SNan Zhou 
751185444b1SNan Zhou     if (aObj == nullptr)
752185444b1SNan Zhou     {
753185444b1SNan Zhou         if (bObj == nullptr)
754185444b1SNan Zhou         {
755185444b1SNan Zhou             return 0;
756185444b1SNan Zhou         }
757185444b1SNan Zhou         return -1;
758185444b1SNan Zhou     }
759185444b1SNan Zhou     if (bObj == nullptr)
760185444b1SNan Zhou     {
761185444b1SNan Zhou         return 1;
762185444b1SNan Zhou     }
7634e196b9aSEd Tanous     object_t::const_iterator aIt = aObj->find(key);
7644e196b9aSEd Tanous     object_t::const_iterator bIt = bObj->find(key);
7654e196b9aSEd Tanous     // If either object doesn't have the key, they get "sorted" to the
7664e196b9aSEd Tanous     // beginning.
767185444b1SNan Zhou     if (aIt == aObj->end())
768185444b1SNan Zhou     {
769185444b1SNan Zhou         if (bIt == bObj->end())
770185444b1SNan Zhou         {
771185444b1SNan Zhou             return 0;
772185444b1SNan Zhou         }
773185444b1SNan Zhou         return -1;
774185444b1SNan Zhou     }
775185444b1SNan Zhou     if (bIt == bObj->end())
776185444b1SNan Zhou     {
777185444b1SNan Zhou         return 1;
778185444b1SNan Zhou     }
779185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
780185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
781185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
782185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
783185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
7844e196b9aSEd Tanous     // the beginning.
785185444b1SNan Zhou     if (nameA == nullptr)
786185444b1SNan Zhou     {
787185444b1SNan Zhou         if (nameB == nullptr)
788185444b1SNan Zhou         {
789185444b1SNan Zhou             return 0;
790185444b1SNan Zhou         }
791185444b1SNan Zhou         return -1;
792185444b1SNan Zhou     }
793185444b1SNan Zhou     if (nameB == nullptr)
794185444b1SNan Zhou     {
795185444b1SNan Zhou         return 1;
796185444b1SNan Zhou     }
797*e7bcf475SJayanth Othayoth     if (key != "@odata.id")
798*e7bcf475SJayanth Othayoth     {
799*e7bcf475SJayanth Othayoth         return alphanumComp(*nameA, *nameB);
800*e7bcf475SJayanth Othayoth     }
801185444b1SNan Zhou 
802*e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> aUrl =
803*e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameA);
804*e7bcf475SJayanth Othayoth     boost::system::result<boost::urls::url_view> bUrl =
805*e7bcf475SJayanth Othayoth         boost::urls::parse_relative_ref(*nameB);
806*e7bcf475SJayanth Othayoth     if (!aUrl)
807185444b1SNan Zhou     {
808*e7bcf475SJayanth Othayoth         if (!bUrl)
809185444b1SNan Zhou         {
810185444b1SNan Zhou             return 0;
811185444b1SNan Zhou         }
812185444b1SNan Zhou         return -1;
813185444b1SNan Zhou     }
814*e7bcf475SJayanth Othayoth     if (!bUrl)
815*e7bcf475SJayanth Othayoth     {
816*e7bcf475SJayanth Othayoth         return 1;
817*e7bcf475SJayanth Othayoth     }
818*e7bcf475SJayanth Othayoth 
819*e7bcf475SJayanth Othayoth     auto segmentsAIt = aUrl->segments().begin();
820*e7bcf475SJayanth Othayoth     auto segmentsBIt = bUrl->segments().begin();
821*e7bcf475SJayanth Othayoth 
822*e7bcf475SJayanth Othayoth     while (true)
823*e7bcf475SJayanth Othayoth     {
824*e7bcf475SJayanth Othayoth         if (segmentsAIt == aUrl->segments().end())
825*e7bcf475SJayanth Othayoth         {
826*e7bcf475SJayanth Othayoth             if (segmentsBIt == bUrl->segments().end())
827*e7bcf475SJayanth Othayoth             {
828*e7bcf475SJayanth Othayoth                 return 0;
829*e7bcf475SJayanth Othayoth             }
830*e7bcf475SJayanth Othayoth             return -1;
831*e7bcf475SJayanth Othayoth         }
832*e7bcf475SJayanth Othayoth         if (segmentsBIt == bUrl->segments().end())
833185444b1SNan Zhou         {
834185444b1SNan Zhou             return 1;
835185444b1SNan Zhou         }
836185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
837185444b1SNan Zhou         if (res != 0)
838185444b1SNan Zhou         {
839185444b1SNan Zhou             return res;
840185444b1SNan Zhou         }
841185444b1SNan Zhou 
842185444b1SNan Zhou         segmentsAIt++;
843185444b1SNan Zhou         segmentsBIt++;
844185444b1SNan Zhou     }
845*e7bcf475SJayanth Othayoth     return 0;
846185444b1SNan Zhou };
847185444b1SNan Zhou 
8484e196b9aSEd Tanous // kept for backward compatibility
8494e196b9aSEd Tanous inline int odataObjectCmp(const nlohmann::json& left,
8504e196b9aSEd Tanous                           const nlohmann::json& right)
8514e196b9aSEd Tanous {
8524e196b9aSEd Tanous     return objectKeyCmp("@odata.id", left, right);
8534e196b9aSEd Tanous }
8544e196b9aSEd Tanous 
855185444b1SNan Zhou struct ODataObjectLess
856185444b1SNan Zhou {
8574e196b9aSEd Tanous     std::string_view key;
8584e196b9aSEd Tanous 
8594e196b9aSEd Tanous     explicit ODataObjectLess(std::string_view keyIn) : key(keyIn) {}
8604e196b9aSEd Tanous 
861185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
862185444b1SNan Zhou                     const nlohmann::json& right) const
863185444b1SNan Zhou     {
8644e196b9aSEd Tanous         return objectKeyCmp(key, left, right) < 0;
865185444b1SNan Zhou     }
866185444b1SNan Zhou };
867185444b1SNan Zhou 
868185444b1SNan Zhou // Sort the JSON array by |element[key]|.
869185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
870185444b1SNan Zhou // those whose |element[key]| is string.
8714e196b9aSEd Tanous inline void sortJsonArrayByKey(nlohmann::json::array_t& array,
8724e196b9aSEd Tanous                                std::string_view key)
8734e196b9aSEd Tanous {
8744e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess(key));
8754e196b9aSEd Tanous }
8764e196b9aSEd Tanous 
8774e196b9aSEd Tanous // Sort the JSON array by |element[key]|.
8784e196b9aSEd Tanous // Elements without |key| or type of |element[key]| is not string are smaller
8794e196b9aSEd Tanous // those whose |element[key]| is string.
880185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
881185444b1SNan Zhou {
8824e196b9aSEd Tanous     std::ranges::sort(array, ODataObjectLess("@odata.id"));
883185444b1SNan Zhou }
884185444b1SNan Zhou 
8858a7c4b47SNan Zhou // Returns the estimated size of the JSON value
8868a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
8878a7c4b47SNan Zhou //  total size of keys and values.
8888a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
8898a7c4b47SNan Zhou 
8908a7c4b47SNan Zhou // Assumption made:
8918a7c4b47SNan Zhou //  1. number: 8 characters
8928a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
8938a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
8948a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
8958a7c4b47SNan Zhou //  5. null: 4 characters (null)
8968a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
8978a7c4b47SNan Zhou 
89877dd8813SKowalski, Kamil } // namespace json_util
89977dd8813SKowalski, Kamil } // namespace redfish
900