xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision 6be832e2963e9d720dd95543358eca380c5e52d2)
177dd8813SKowalski, Kamil /*
2*6be832e2SEd Tanous Copyright (c) 2018 Intel Corporation
3*6be832e2SEd Tanous 
4*6be832e2SEd Tanous Licensed under the Apache License, Version 2.0 (the "License");
5*6be832e2SEd Tanous you may not use this file except in compliance with the License.
6*6be832e2SEd Tanous You may obtain a copy of the License at
7*6be832e2SEd Tanous 
8*6be832e2SEd Tanous       http://www.apache.org/licenses/LICENSE-2.0
9*6be832e2SEd Tanous 
10*6be832e2SEd Tanous Unless required by applicable law or agreed to in writing, software
11*6be832e2SEd Tanous distributed under the License is distributed on an "AS IS" BASIS,
12*6be832e2SEd Tanous WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6be832e2SEd Tanous See the License for the specific language governing permissions and
14*6be832e2SEd 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 
25faf100f9SEd Tanous #include <nlohmann/json.hpp>
260627a2c7SEd Tanous 
27185444b1SNan Zhou #include <algorithm>
28d5c80ad9SNan Zhou #include <array>
29d5c80ad9SNan Zhou #include <cmath>
30d5c80ad9SNan Zhou #include <cstddef>
31d5c80ad9SNan Zhou #include <cstdint>
32d5c80ad9SNan Zhou #include <limits>
33d5c80ad9SNan Zhou #include <map>
34d5c80ad9SNan Zhou #include <optional>
353544d2a7SEd Tanous #include <ranges>
36ea2e6eecSWilly Tu #include <span>
37d5c80ad9SNan Zhou #include <string>
38d5c80ad9SNan Zhou #include <string_view>
39d5c80ad9SNan Zhou #include <type_traits>
40d5c80ad9SNan Zhou #include <utility>
41d5c80ad9SNan Zhou #include <variant>
42d5c80ad9SNan Zhou #include <vector>
43d5c80ad9SNan Zhou 
441e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request
451214b7e7SGunnar Mills 
461abe55efSEd Tanous namespace redfish
471abe55efSEd Tanous {
481abe55efSEd Tanous 
491abe55efSEd Tanous namespace json_util
501abe55efSEd Tanous {
5177dd8813SKowalski, Kamil 
5277dd8813SKowalski, Kamil /**
5377dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
5477dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
5577dd8813SKowalski, Kamil  *
5677dd8813SKowalski, Kamil  * @param[io]  res       Response object
5777dd8813SKowalski, Kamil  * @param[in]  req       Request object
5877dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
5977dd8813SKowalski, Kamil  *
6077dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
6177dd8813SKowalski, Kamil  *         been filled with message and ended.
6277dd8813SKowalski, Kamil  */
6355c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
6477dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
659712f8acSEd Tanous namespace details
669712f8acSEd Tanous {
67771cfa0fSJason M. Bills 
681214b7e7SGunnar Mills template <typename Type>
692c70f800SEd Tanous struct IsOptional : std::false_type
701214b7e7SGunnar Mills {};
719712f8acSEd Tanous 
72771cfa0fSJason M. Bills template <typename Type>
732c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
741214b7e7SGunnar Mills {};
759712f8acSEd Tanous 
76771cfa0fSJason M. Bills template <typename Type>
772c70f800SEd Tanous struct IsVector : std::false_type
781214b7e7SGunnar Mills {};
79b1556427SEd Tanous 
801214b7e7SGunnar Mills template <typename Type>
812c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
821214b7e7SGunnar Mills {};
83b1556427SEd Tanous 
841214b7e7SGunnar Mills template <typename Type>
852c70f800SEd Tanous struct IsStdArray : std::false_type
861214b7e7SGunnar Mills {};
87318226c2SJames Feist 
88318226c2SJames Feist template <typename Type, std::size_t size>
892c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
901214b7e7SGunnar Mills {};
91318226c2SJames Feist 
928099c517SEd Tanous template <typename Type>
938099c517SEd Tanous struct IsVariant : std::false_type
948099c517SEd Tanous {};
958099c517SEd Tanous 
968099c517SEd Tanous template <typename... Types>
978099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type
988099c517SEd Tanous {};
998099c517SEd Tanous 
100471a5eb8SAppaRao Puli enum class UnpackErrorCode
101471a5eb8SAppaRao Puli {
102471a5eb8SAppaRao Puli     success,
103471a5eb8SAppaRao Puli     invalidType,
104471a5eb8SAppaRao Puli     outOfRange
105471a5eb8SAppaRao Puli };
106471a5eb8SAppaRao Puli 
107a6acbb31SJames Feist template <typename ToType, typename FromType>
108c09966bdSEd Tanous bool checkRange(const FromType& from [[maybe_unused]],
109c09966bdSEd Tanous                 std::string_view key [[maybe_unused]])
110a6acbb31SJames Feist {
111a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
112a6acbb31SJames Feist     {
113ee344e0fSEd Tanous         if (std::isnan(from))
114a6acbb31SJames Feist         {
11562598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
116a6acbb31SJames Feist             return false;
117a6acbb31SJames Feist         }
118a6acbb31SJames Feist     }
119c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::max() <
120c09966bdSEd Tanous                   std::numeric_limits<FromType>::max())
121c09966bdSEd Tanous     {
122c09966bdSEd Tanous         if (from > std::numeric_limits<ToType>::max())
123c09966bdSEd Tanous         {
124c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
125c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
126c09966bdSEd Tanous             return false;
127c09966bdSEd Tanous         }
128c09966bdSEd Tanous     }
129c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::lowest() >
130c09966bdSEd Tanous                   std::numeric_limits<FromType>::lowest())
131c09966bdSEd Tanous     {
132c09966bdSEd Tanous         if (from < std::numeric_limits<ToType>::lowest())
133c09966bdSEd Tanous         {
134c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
135c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
136c09966bdSEd Tanous             return false;
137c09966bdSEd Tanous         }
138c09966bdSEd Tanous     }
139a6acbb31SJames Feist 
140a6acbb31SJames Feist     return true;
141a6acbb31SJames Feist }
142a6acbb31SJames Feist 
143771cfa0fSJason M. Bills template <typename Type>
144471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1458099c517SEd Tanous                                          std::string_view key, Type& value);
1468099c517SEd Tanous 
1478099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1488099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1498099c517SEd Tanous                                    std::variant<Args...>& v)
1508099c517SEd Tanous {
1518099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1528099c517SEd Tanous     {
153ed4de7a8SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type{};
1548099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1558099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1568099c517SEd Tanous         {
1578099c517SEd Tanous             v = std::move(type);
1588099c517SEd Tanous             return unpack;
1598099c517SEd Tanous         }
1608099c517SEd Tanous 
1618099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1628099c517SEd Tanous     }
1638099c517SEd Tanous     return UnpackErrorCode::invalidType;
1648099c517SEd Tanous }
1658099c517SEd Tanous 
1668099c517SEd Tanous template <typename Type>
1678099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
168ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
169771cfa0fSJason M. Bills {
170471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
17141352c24SSantosh Puranik 
172a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
173771cfa0fSJason M. Bills     {
174a6acbb31SJames Feist         double helper = 0;
175a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
176771cfa0fSJason M. Bills 
177771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
178771cfa0fSJason M. Bills         {
179a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
180a6acbb31SJames Feist             if (intPtr != nullptr)
181771cfa0fSJason M. Bills             {
182a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
183a6acbb31SJames Feist                 jsonPtr = &helper;
184771cfa0fSJason M. Bills             }
185a6acbb31SJames Feist         }
1865eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1875eb2bef2SAppaRao Puli         {
188471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1895eb2bef2SAppaRao Puli         }
190cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
191771cfa0fSJason M. Bills         {
192471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
193771cfa0fSJason M. Bills         }
194771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
195771cfa0fSJason M. Bills     }
196a6acbb31SJames Feist 
197a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
198a6acbb31SJames Feist     {
199a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
200271584abSEd Tanous         if (jsonPtr == nullptr)
201271584abSEd Tanous         {
202471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
203271584abSEd Tanous         }
204cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
205a6acbb31SJames Feist         {
206471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
207a6acbb31SJames Feist         }
208a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
209a6acbb31SJames Feist     }
210a6acbb31SJames Feist 
211bd79bce8SPatrick Williams     else if constexpr ((std::is_unsigned_v<Type>) &&
212bd79bce8SPatrick Williams                        (!std::is_same_v<bool, Type>))
213a6acbb31SJames Feist     {
214a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
215271584abSEd Tanous         if (jsonPtr == nullptr)
216271584abSEd Tanous         {
217471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
218271584abSEd Tanous         }
219cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
220a6acbb31SJames Feist         {
221471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
222a6acbb31SJames Feist         }
223a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
224a6acbb31SJames Feist     }
225a6acbb31SJames Feist 
2260627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2270627a2c7SEd Tanous     {
2280627a2c7SEd Tanous         value = std::move(jsonValue);
2290627a2c7SEd Tanous     }
2308099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2318099c517SEd Tanous     {
2328099c517SEd Tanous         if (!jsonValue.is_null())
2338099c517SEd Tanous         {
2348099c517SEd Tanous             return UnpackErrorCode::invalidType;
2358099c517SEd Tanous         }
2368099c517SEd Tanous     }
237ed4de7a8SEd Tanous     else if constexpr (IsVector<Type>::value)
238ed4de7a8SEd Tanous     {
239ed4de7a8SEd Tanous         nlohmann::json::object_t* obj =
240ed4de7a8SEd Tanous             jsonValue.get_ptr<nlohmann::json::object_t*>();
241ed4de7a8SEd Tanous         if (obj == nullptr)
242ed4de7a8SEd Tanous         {
243ed4de7a8SEd Tanous             return UnpackErrorCode::invalidType;
244ed4de7a8SEd Tanous         }
245ed4de7a8SEd Tanous 
246ed4de7a8SEd Tanous         for (const auto& val : *obj)
247ed4de7a8SEd Tanous         {
248ed4de7a8SEd Tanous             value.emplace_back();
249ed4de7a8SEd Tanous             ret = unpackValueWithErrorCode<typename Type::value_type>(
250ed4de7a8SEd Tanous                       val, key, value.back()) &&
251ed4de7a8SEd Tanous                   ret;
252ed4de7a8SEd Tanous         }
253ed4de7a8SEd Tanous     }
254471a5eb8SAppaRao Puli     else
255471a5eb8SAppaRao Puli     {
256471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
257471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
258471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
259471a5eb8SAppaRao Puli         {
26062598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
26162598e31SEd Tanous                              jsonValue.type_name());
262471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
263471a5eb8SAppaRao Puli         }
264471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
265471a5eb8SAppaRao Puli     }
266471a5eb8SAppaRao Puli     return ret;
267471a5eb8SAppaRao Puli }
268471a5eb8SAppaRao Puli 
269471a5eb8SAppaRao Puli template <typename Type>
270ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
271471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
272471a5eb8SAppaRao Puli {
273471a5eb8SAppaRao Puli     bool ret = true;
274471a5eb8SAppaRao Puli 
2752c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
276471a5eb8SAppaRao Puli     {
277471a5eb8SAppaRao Puli         value.emplace();
278471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
279471a5eb8SAppaRao Puli                                                      *value) &&
280471a5eb8SAppaRao Puli               ret;
281471a5eb8SAppaRao Puli     }
2822c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
283318226c2SJames Feist     {
2840bdda665SEd Tanous         nlohmann::json::array_t* arr =
2850bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
2860bdda665SEd Tanous         if (arr == nullptr)
287318226c2SJames Feist         {
2882e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
28941352c24SSantosh Puranik             return false;
290318226c2SJames Feist         }
291318226c2SJames Feist         if (jsonValue.size() != value.size())
292318226c2SJames Feist         {
2932e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
29441352c24SSantosh Puranik             return false;
295318226c2SJames Feist         }
296318226c2SJames Feist         size_t index = 0;
2970bdda665SEd Tanous         for (auto& val : *arr)
298318226c2SJames Feist         {
2990bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
30041352c24SSantosh Puranik                                                          value[index++]) &&
30141352c24SSantosh Puranik                   ret;
302318226c2SJames Feist         }
303318226c2SJames Feist     }
3042c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
305b1556427SEd Tanous     {
3060bdda665SEd Tanous         nlohmann::json::array_t* arr =
3070bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3080bdda665SEd Tanous         if (arr == nullptr)
309b1556427SEd Tanous         {
3102e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
31141352c24SSantosh Puranik             return false;
312b1556427SEd Tanous         }
313b1556427SEd Tanous 
3140bdda665SEd Tanous         for (auto& val : *arr)
315b1556427SEd Tanous         {
316b1556427SEd Tanous             value.emplace_back();
3170bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key, res,
31841352c24SSantosh Puranik                                                          value.back()) &&
31941352c24SSantosh Puranik                   ret;
320b1556427SEd Tanous         }
321b1556427SEd Tanous     }
3228099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3238099c517SEd Tanous     {
3248099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3258099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3268099c517SEd Tanous         {
3278099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3288099c517SEd Tanous             {
3298099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3308099c517SEd Tanous             }
3318099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3328099c517SEd Tanous             {
3338099c517SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
3348099c517SEd Tanous             }
3358099c517SEd Tanous             return false;
3368099c517SEd Tanous         }
3378099c517SEd Tanous     }
338771cfa0fSJason M. Bills     else
339771cfa0fSJason M. Bills     {
340471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
341471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
342771cfa0fSJason M. Bills         {
343471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
344471a5eb8SAppaRao Puli             {
3452e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
346471a5eb8SAppaRao Puli             }
347471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
348471a5eb8SAppaRao Puli             {
349e2616cc5SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
350471a5eb8SAppaRao Puli             }
35141352c24SSantosh Puranik             return false;
352771cfa0fSJason M. Bills         }
353771cfa0fSJason M. Bills     }
354471a5eb8SAppaRao Puli 
355471a5eb8SAppaRao Puli     return ret;
356471a5eb8SAppaRao Puli }
357471a5eb8SAppaRao Puli 
358471a5eb8SAppaRao Puli template <typename Type>
359ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
360471a5eb8SAppaRao Puli {
361471a5eb8SAppaRao Puli     bool ret = true;
3622c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
363471a5eb8SAppaRao Puli     {
364471a5eb8SAppaRao Puli         value.emplace();
365471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
366471a5eb8SAppaRao Puli               ret;
367471a5eb8SAppaRao Puli     }
3682c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
369471a5eb8SAppaRao Puli     {
3700bdda665SEd Tanous         nlohmann::json::array_t* arr =
3710bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3720bdda665SEd Tanous         if (arr == nullptr)
373471a5eb8SAppaRao Puli         {
374471a5eb8SAppaRao Puli             return false;
375471a5eb8SAppaRao Puli         }
376471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
377471a5eb8SAppaRao Puli         {
378471a5eb8SAppaRao Puli             return false;
379471a5eb8SAppaRao Puli         }
380471a5eb8SAppaRao Puli         size_t index = 0;
3810bdda665SEd Tanous         for (const auto& val : *arr)
382471a5eb8SAppaRao Puli         {
3830bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
384471a5eb8SAppaRao Puli                                                          value[index++]) &&
385471a5eb8SAppaRao Puli                   ret;
386471a5eb8SAppaRao Puli         }
387471a5eb8SAppaRao Puli     }
3882c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
389471a5eb8SAppaRao Puli     {
3900bdda665SEd Tanous         nlohmann::json::array_t* arr =
3910bdda665SEd Tanous             jsonValue.get_ptr<nlohmann::json::array_t*>();
3920bdda665SEd Tanous         if (arr == nullptr)
393471a5eb8SAppaRao Puli         {
394471a5eb8SAppaRao Puli             return false;
395471a5eb8SAppaRao Puli         }
396471a5eb8SAppaRao Puli 
3970bdda665SEd Tanous         for (const auto& val : *arr)
398471a5eb8SAppaRao Puli         {
399471a5eb8SAppaRao Puli             value.emplace_back();
4000bdda665SEd Tanous             ret = unpackValue<typename Type::value_type>(val, key,
401471a5eb8SAppaRao Puli                                                          value.back()) &&
402471a5eb8SAppaRao Puli                   ret;
403471a5eb8SAppaRao Puli         }
404471a5eb8SAppaRao Puli     }
405471a5eb8SAppaRao Puli     else
406471a5eb8SAppaRao Puli     {
407471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
408471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
409471a5eb8SAppaRao Puli         {
410471a5eb8SAppaRao Puli             return false;
411471a5eb8SAppaRao Puli         }
412471a5eb8SAppaRao Puli     }
413471a5eb8SAppaRao Puli 
41441352c24SSantosh Puranik     return ret;
415771cfa0fSJason M. Bills }
4169712f8acSEd Tanous } // namespace details
4179712f8acSEd Tanous 
418ea2e6eecSWilly Tu // clang-format off
419ea2e6eecSWilly Tu using UnpackVariant = std::variant<
420ea2e6eecSWilly Tu     uint8_t*,
421ea2e6eecSWilly Tu     uint16_t*,
422ea2e6eecSWilly Tu     int16_t*,
423ea2e6eecSWilly Tu     uint32_t*,
424ea2e6eecSWilly Tu     int32_t*,
425ea2e6eecSWilly Tu     uint64_t*,
426ea2e6eecSWilly Tu     int64_t*,
427ea2e6eecSWilly Tu     bool*,
428ea2e6eecSWilly Tu     double*,
429ea2e6eecSWilly Tu     std::string*,
430b6164cbeSEd Tanous     nlohmann::json::object_t*,
4318099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4328099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4338099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4348099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4358099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4368099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4378099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4388099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4398099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4408099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
441ea2e6eecSWilly Tu     std::vector<uint8_t>*,
442ea2e6eecSWilly Tu     std::vector<uint16_t>*,
443ea2e6eecSWilly Tu     std::vector<int16_t>*,
444ea2e6eecSWilly Tu     std::vector<uint32_t>*,
445ea2e6eecSWilly Tu     std::vector<int32_t>*,
446ea2e6eecSWilly Tu     std::vector<uint64_t>*,
447ea2e6eecSWilly Tu     std::vector<int64_t>*,
448ea2e6eecSWilly Tu     //std::vector<bool>*,
449ea2e6eecSWilly Tu     std::vector<double>*,
450ea2e6eecSWilly Tu     std::vector<std::string>*,
451b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
452ea2e6eecSWilly Tu     std::optional<uint8_t>*,
453ea2e6eecSWilly Tu     std::optional<uint16_t>*,
454ea2e6eecSWilly Tu     std::optional<int16_t>*,
455ea2e6eecSWilly Tu     std::optional<uint32_t>*,
456ea2e6eecSWilly Tu     std::optional<int32_t>*,
457ea2e6eecSWilly Tu     std::optional<uint64_t>*,
458ea2e6eecSWilly Tu     std::optional<int64_t>*,
459ea2e6eecSWilly Tu     std::optional<bool>*,
460ea2e6eecSWilly Tu     std::optional<double>*,
461ea2e6eecSWilly Tu     std::optional<std::string>*,
462b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
463ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
464ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
465ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
466ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
467ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
468ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
469ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
470ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
471ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
472ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
4738099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4748099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4758099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4768099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4778099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4788099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4798099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4808099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
4818099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
4828099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
4838099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
4848099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
485ed4de7a8SEd Tanous     std::optional<std::vector<std::variant<std::string, nlohmann::json::object_t, std::nullptr_t>>>*,
486ed4de7a8SEd Tanous 
487ed4de7a8SEd Tanous     // Note, these types are kept for historical completeness, but should not be used,
488ed4de7a8SEd Tanous     // As they do not provide object type safety.  Instead, rely on nlohmann::json::object_t
489ed4de7a8SEd Tanous     // Will be removed Q2 2025
490ed4de7a8SEd Tanous     nlohmann::json*,
491ed4de7a8SEd Tanous     std::optional<std::vector<nlohmann::json>>*,
492ed4de7a8SEd Tanous     std::vector<nlohmann::json>*,
493ed4de7a8SEd Tanous     std::optional<nlohmann::json>*
494ea2e6eecSWilly Tu >;
495ea2e6eecSWilly Tu // clang-format on
496ea2e6eecSWilly Tu 
497ea2e6eecSWilly Tu struct PerUnpack
498ea2e6eecSWilly Tu {
499ea2e6eecSWilly Tu     std::string_view key;
500ea2e6eecSWilly Tu     UnpackVariant value;
501ea2e6eecSWilly Tu     bool complete = false;
502ea2e6eecSWilly Tu };
503ea2e6eecSWilly Tu 
504ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
505b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack);
506b6164cbeSEd Tanous 
507b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
508b6164cbeSEd Tanous                                  crow::Response& res,
509ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
5109712f8acSEd Tanous {
51141352c24SSantosh Puranik     bool result = true;
512b6164cbeSEd Tanous     for (auto& item : obj)
5139712f8acSEd Tanous     {
514ea2e6eecSWilly Tu         size_t unpackIndex = 0;
515ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
516ea2e6eecSWilly Tu         {
517ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
518ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
519ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
520ea2e6eecSWilly Tu             std::string_view leftover;
521ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
522ea2e6eecSWilly Tu             {
523ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
524ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
525ea2e6eecSWilly Tu             }
526ea2e6eecSWilly Tu 
527d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
528ea2e6eecSWilly Tu             {
529ea2e6eecSWilly Tu                 continue;
530ea2e6eecSWilly Tu             }
531ea2e6eecSWilly Tu 
532ea2e6eecSWilly Tu             // Sublevel key
533ea2e6eecSWilly Tu             if (!leftover.empty())
534ea2e6eecSWilly Tu             {
535ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
536ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
537ea2e6eecSWilly Tu                 nlohmann::json j;
538d91415c4SEd Tanous                 result = details::unpackValue<nlohmann::json>(item.second, key,
539ea2e6eecSWilly Tu                                                               res, j) &&
54041352c24SSantosh Puranik                          result;
54155f79e6fSEd Tanous                 if (!result)
542ea2e6eecSWilly Tu                 {
543ea2e6eecSWilly Tu                     return result;
5449712f8acSEd Tanous                 }
5459712f8acSEd Tanous 
546ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
547ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
548ea2e6eecSWilly Tu                 {
549ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
550ea2e6eecSWilly Tu                     {
551ea2e6eecSWilly Tu                         continue;
552ea2e6eecSWilly Tu                     }
553ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
554ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
555ea2e6eecSWilly Tu                     p.complete = true;
5569712f8acSEd Tanous                 }
55777dd8813SKowalski, Kamil 
558ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
559ea2e6eecSWilly Tu                 break;
560ea2e6eecSWilly Tu             }
561ea2e6eecSWilly Tu 
562bd79bce8SPatrick Williams             result =
563bd79bce8SPatrick Williams                 std::visit(
5645ea927bbSEd Tanous                     [&item, &unpackSpec, &res](auto& val) {
565ea2e6eecSWilly Tu                         using ContainedT =
566ea2e6eecSWilly Tu                             std::remove_pointer_t<std::decay_t<decltype(val)>>;
567ea2e6eecSWilly Tu                         return details::unpackValue<ContainedT>(
568d91415c4SEd Tanous                             item.second, unpackSpec.key, res, *val);
569ea2e6eecSWilly Tu                     },
570ea2e6eecSWilly Tu                     unpackSpec.value) &&
571ea2e6eecSWilly Tu                 result;
572ea2e6eecSWilly Tu 
573ea2e6eecSWilly Tu             unpackSpec.complete = true;
574ea2e6eecSWilly Tu             break;
575ea2e6eecSWilly Tu         }
576ea2e6eecSWilly Tu 
577ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
578ea2e6eecSWilly Tu         {
579d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
580ea2e6eecSWilly Tu             result = false;
581ea2e6eecSWilly Tu         }
582ea2e6eecSWilly Tu     }
583ea2e6eecSWilly Tu 
584ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
585ea2e6eecSWilly Tu     {
58655f79e6fSEd Tanous         if (!perUnpack.complete)
587ea2e6eecSWilly Tu         {
588ea2e6eecSWilly Tu             bool isOptional = std::visit(
5895ea927bbSEd Tanous                 [](auto& val) {
590ea2e6eecSWilly Tu                     using ContainedType =
591ea2e6eecSWilly Tu                         std::remove_pointer_t<std::decay_t<decltype(val)>>;
592ea2e6eecSWilly Tu                     return details::IsOptional<ContainedType>::value;
593ea2e6eecSWilly Tu                 },
594ea2e6eecSWilly Tu                 perUnpack.value);
595ea2e6eecSWilly Tu             if (isOptional)
596ea2e6eecSWilly Tu             {
597ea2e6eecSWilly Tu                 continue;
598ea2e6eecSWilly Tu             }
599ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
600ea2e6eecSWilly Tu             result = false;
601ea2e6eecSWilly Tu         }
602ea2e6eecSWilly Tu     }
603ea2e6eecSWilly Tu     return result;
604ea2e6eecSWilly Tu }
605ea2e6eecSWilly Tu 
606b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
607b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack)
608b6164cbeSEd Tanous {
609b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
610b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
611b6164cbeSEd Tanous     if (obj == nullptr)
612b6164cbeSEd Tanous     {
613b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
614b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
615b6164cbeSEd Tanous         return false;
616b6164cbeSEd Tanous     }
617b6164cbeSEd Tanous     return readJsonHelperObject(*obj, res, toUnpack);
618b6164cbeSEd Tanous }
619b6164cbeSEd Tanous 
62089492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
621ea2e6eecSWilly Tu 
622ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
623ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
6245ea927bbSEd Tanous                  FirstType&& first, UnpackTypes&&... in)
625ea2e6eecSWilly Tu {
626ea2e6eecSWilly Tu     if (toPack.empty())
627ea2e6eecSWilly Tu     {
628ea2e6eecSWilly Tu         return;
629ea2e6eecSWilly Tu     }
630ea2e6eecSWilly Tu     toPack[0].key = key;
631ea2e6eecSWilly Tu     toPack[0].value = &first;
632ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
633ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
634ea2e6eecSWilly Tu }
635ea2e6eecSWilly Tu 
636ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
637b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
638b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
639b6164cbeSEd Tanous                     UnpackTypes&&... in)
640ea2e6eecSWilly Tu {
641ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
642ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
6435ea927bbSEd Tanous     packVariant(toUnpack2, key, std::forward<FirstType>(first),
6445ea927bbSEd Tanous                 std::forward<UnpackTypes&&>(in)...);
645b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
646ea2e6eecSWilly Tu }
647ea2e6eecSWilly Tu 
648b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
649b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
650b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
651b6164cbeSEd Tanous {
652b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
653b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
654b6164cbeSEd Tanous     if (obj == nullptr)
655b6164cbeSEd Tanous     {
656b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
657b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
658b6164cbeSEd Tanous         return false;
659b6164cbeSEd Tanous     }
6605be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6615be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
662b6164cbeSEd Tanous }
663b6164cbeSEd Tanous 
664b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t>
665ea2e6eecSWilly Tu     readJsonPatchHelper(const crow::Request& req, crow::Response& res)
6660627a2c7SEd Tanous {
6670627a2c7SEd Tanous     nlohmann::json jsonRequest;
6680627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6690627a2c7SEd Tanous     {
67062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
671ea2e6eecSWilly Tu         return std::nullopt;
6720627a2c7SEd Tanous     }
673357bb8f8SEd Tanous     nlohmann::json::object_t* object =
674357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
675357bb8f8SEd Tanous     if (object == nullptr || object->empty())
67615ed6780SWilly Tu     {
67762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
67815ed6780SWilly Tu         messages::emptyJSON(res);
679ea2e6eecSWilly Tu         return std::nullopt;
680ea2e6eecSWilly Tu     }
681357bb8f8SEd Tanous     std::erase_if(*object,
682357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
683357bb8f8SEd Tanous                       return item.first.starts_with("@odata.");
684357bb8f8SEd Tanous                   });
685357bb8f8SEd Tanous     if (object->empty())
686357bb8f8SEd Tanous     {
687357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
688357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
689357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
690357bb8f8SEd Tanous         messages::noOperation(res);
691357bb8f8SEd Tanous         return std::nullopt;
692357bb8f8SEd Tanous     }
693357bb8f8SEd Tanous 
694b6164cbeSEd Tanous     return {std::move(*object)};
695ea2e6eecSWilly Tu }
696ea2e6eecSWilly Tu 
697ea2e6eecSWilly Tu template <typename... UnpackTypes>
698ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
699ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
700ea2e6eecSWilly Tu {
70176b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
70276b038f2SEd Tanous         readJsonPatchHelper(req, res);
703e01d0c36SEd Tanous     if (!jsonRequest)
704ea2e6eecSWilly Tu     {
70515ed6780SWilly Tu         return false;
70615ed6780SWilly Tu     }
70776b038f2SEd Tanous     if (jsonRequest->empty())
708b6164cbeSEd Tanous     {
709b6164cbeSEd Tanous         messages::emptyJSON(res);
710b6164cbeSEd Tanous         return false;
711b6164cbeSEd Tanous     }
71215ed6780SWilly Tu 
71376b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
714b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
71515ed6780SWilly Tu }
71615ed6780SWilly Tu 
71715ed6780SWilly Tu template <typename... UnpackTypes>
71815ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
719ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
72015ed6780SWilly Tu {
72115ed6780SWilly Tu     nlohmann::json jsonRequest;
72215ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
72315ed6780SWilly Tu     {
72462598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
72515ed6780SWilly Tu         return false;
72615ed6780SWilly Tu     }
727b6164cbeSEd Tanous     nlohmann::json::object_t* object =
728b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
729b6164cbeSEd Tanous     if (object == nullptr)
730b6164cbeSEd Tanous     {
731b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
732b6164cbeSEd Tanous         messages::emptyJSON(res);
733b6164cbeSEd Tanous         return false;
734b6164cbeSEd Tanous     }
735b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
736b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7370627a2c7SEd Tanous }
738185444b1SNan Zhou 
739185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
740185444b1SNan Zhou // @odata.id key
741185444b1SNan Zhou inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b)
742185444b1SNan Zhou {
743185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
744185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
745185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
746185444b1SNan Zhou 
747185444b1SNan Zhou     if (aObj == nullptr)
748185444b1SNan Zhou     {
749185444b1SNan Zhou         if (bObj == nullptr)
750185444b1SNan Zhou         {
751185444b1SNan Zhou             return 0;
752185444b1SNan Zhou         }
753185444b1SNan Zhou         return -1;
754185444b1SNan Zhou     }
755185444b1SNan Zhou     if (bObj == nullptr)
756185444b1SNan Zhou     {
757185444b1SNan Zhou         return 1;
758185444b1SNan Zhou     }
759185444b1SNan Zhou     object_t::const_iterator aIt = aObj->find("@odata.id");
760185444b1SNan Zhou     object_t::const_iterator bIt = bObj->find("@odata.id");
761185444b1SNan Zhou     // If either object doesn't have the key, they get "sorted" to the end.
762185444b1SNan Zhou     if (aIt == aObj->end())
763185444b1SNan Zhou     {
764185444b1SNan Zhou         if (bIt == bObj->end())
765185444b1SNan Zhou         {
766185444b1SNan Zhou             return 0;
767185444b1SNan Zhou         }
768185444b1SNan Zhou         return -1;
769185444b1SNan Zhou     }
770185444b1SNan Zhou     if (bIt == bObj->end())
771185444b1SNan Zhou     {
772185444b1SNan Zhou         return 1;
773185444b1SNan Zhou     }
774185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
775185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
776185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
777185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
778185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
779185444b1SNan Zhou     // the end.
780185444b1SNan Zhou     if (nameA == nullptr)
781185444b1SNan Zhou     {
782185444b1SNan Zhou         if (nameB == nullptr)
783185444b1SNan Zhou         {
784185444b1SNan Zhou             return 0;
785185444b1SNan Zhou         }
786185444b1SNan Zhou         return -1;
787185444b1SNan Zhou     }
788185444b1SNan Zhou     if (nameB == nullptr)
789185444b1SNan Zhou     {
790185444b1SNan Zhou         return 1;
791185444b1SNan Zhou     }
792185444b1SNan Zhou     boost::urls::url_view aUrl(*nameA);
793185444b1SNan Zhou     boost::urls::url_view bUrl(*nameB);
794185444b1SNan Zhou     auto segmentsAIt = aUrl.segments().begin();
795185444b1SNan Zhou     auto segmentsBIt = bUrl.segments().begin();
796185444b1SNan Zhou 
797185444b1SNan Zhou     while (true)
798185444b1SNan Zhou     {
799185444b1SNan Zhou         if (segmentsAIt == aUrl.segments().end())
800185444b1SNan Zhou         {
801185444b1SNan Zhou             if (segmentsBIt == bUrl.segments().end())
802185444b1SNan Zhou             {
803185444b1SNan Zhou                 return 0;
804185444b1SNan Zhou             }
805185444b1SNan Zhou             return -1;
806185444b1SNan Zhou         }
807185444b1SNan Zhou         if (segmentsBIt == bUrl.segments().end())
808185444b1SNan Zhou         {
809185444b1SNan Zhou             return 1;
810185444b1SNan Zhou         }
811185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
812185444b1SNan Zhou         if (res != 0)
813185444b1SNan Zhou         {
814185444b1SNan Zhou             return res;
815185444b1SNan Zhou         }
816185444b1SNan Zhou 
817185444b1SNan Zhou         segmentsAIt++;
818185444b1SNan Zhou         segmentsBIt++;
819185444b1SNan Zhou     }
820185444b1SNan Zhou };
821185444b1SNan Zhou 
822185444b1SNan Zhou struct ODataObjectLess
823185444b1SNan Zhou {
824185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
825185444b1SNan Zhou                     const nlohmann::json& right) const
826185444b1SNan Zhou     {
827185444b1SNan Zhou         return odataObjectCmp(left, right) < 0;
828185444b1SNan Zhou     }
829185444b1SNan Zhou };
830185444b1SNan Zhou 
831185444b1SNan Zhou // Sort the JSON array by |element[key]|.
832185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
833185444b1SNan Zhou // those whose |element[key]| is string.
834185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
835185444b1SNan Zhou {
8363544d2a7SEd Tanous     std::ranges::sort(array, ODataObjectLess());
837185444b1SNan Zhou }
838185444b1SNan Zhou 
8398a7c4b47SNan Zhou // Returns the estimated size of the JSON value
8408a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
8418a7c4b47SNan Zhou //  total size of keys and values.
8428a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
8438a7c4b47SNan Zhou 
8448a7c4b47SNan Zhou // Assumption made:
8458a7c4b47SNan Zhou //  1. number: 8 characters
8468a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
8478a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
8488a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
8498a7c4b47SNan Zhou //  5. null: 4 characters (null)
8508a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
8518a7c4b47SNan Zhou 
85277dd8813SKowalski, Kamil } // namespace json_util
85377dd8813SKowalski, Kamil } // namespace redfish
854