1*38f85004SShawn McCarney /** 2*38f85004SShawn McCarney * Copyright © 2025 IBM Corporation 3*38f85004SShawn McCarney * 4*38f85004SShawn McCarney * Licensed under the Apache License, Version 2.0 (the "License"); 5*38f85004SShawn McCarney * you may not use this file except in compliance with the License. 6*38f85004SShawn McCarney * You may obtain a copy of the License at 7*38f85004SShawn McCarney * 8*38f85004SShawn McCarney * http://www.apache.org/licenses/LICENSE-2.0 9*38f85004SShawn McCarney * 10*38f85004SShawn McCarney * Unless required by applicable law or agreed to in writing, software 11*38f85004SShawn McCarney * distributed under the License is distributed on an "AS IS" BASIS, 12*38f85004SShawn McCarney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*38f85004SShawn McCarney * See the License for the specific language governing permissions and 14*38f85004SShawn McCarney * limitations under the License. 15*38f85004SShawn McCarney */ 16*38f85004SShawn McCarney #pragma once 17*38f85004SShawn McCarney 18*38f85004SShawn McCarney #include <nlohmann/json.hpp> 19*38f85004SShawn McCarney 20*38f85004SShawn McCarney #include <cstdint> 21*38f85004SShawn McCarney #include <stdexcept> 22*38f85004SShawn McCarney #include <string> 23*38f85004SShawn McCarney #include <vector> 24*38f85004SShawn McCarney 25*38f85004SShawn McCarney /** 26*38f85004SShawn McCarney * @namespace json_parser_utils 27*38f85004SShawn McCarney * 28*38f85004SShawn McCarney * Contains utility functions for parsing JSON data. 29*38f85004SShawn McCarney */ 30*38f85004SShawn McCarney namespace phosphor::power::json_parser_utils 31*38f85004SShawn McCarney { 32*38f85004SShawn McCarney 33*38f85004SShawn McCarney /** 34*38f85004SShawn McCarney * Returns the specified property of the specified JSON element. 35*38f85004SShawn McCarney * 36*38f85004SShawn McCarney * Throws an invalid_argument exception if the property does not exist. 37*38f85004SShawn McCarney * 38*38f85004SShawn McCarney * @param element JSON element 39*38f85004SShawn McCarney * @param property property name 40*38f85004SShawn McCarney */ 41*38f85004SShawn McCarney #pragma GCC diagnostic push 42*38f85004SShawn McCarney #if __GNUC__ >= 13 43*38f85004SShawn McCarney #pragma GCC diagnostic ignored "-Wdangling-reference" 44*38f85004SShawn McCarney #endif 45*38f85004SShawn McCarney inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element, 46*38f85004SShawn McCarney const std::string& property) 47*38f85004SShawn McCarney { 48*38f85004SShawn McCarney auto it = element.find(property); 49*38f85004SShawn McCarney if (it == element.end()) 50*38f85004SShawn McCarney { 51*38f85004SShawn McCarney throw std::invalid_argument{"Required property missing: " + property}; 52*38f85004SShawn McCarney } 53*38f85004SShawn McCarney return *it; 54*38f85004SShawn McCarney } 55*38f85004SShawn McCarney #pragma GCC diagnostic pop 56*38f85004SShawn McCarney 57*38f85004SShawn McCarney /** 58*38f85004SShawn McCarney * Parses a JSON element containing a bit position (from 0-7). 59*38f85004SShawn McCarney * 60*38f85004SShawn McCarney * Returns the corresponding C++ uint8_t value. 61*38f85004SShawn McCarney * 62*38f85004SShawn McCarney * Throws an exception if parsing fails. 63*38f85004SShawn McCarney * 64*38f85004SShawn McCarney * @param element JSON element 65*38f85004SShawn McCarney * @return uint8_t value 66*38f85004SShawn McCarney */ 67*38f85004SShawn McCarney inline uint8_t parseBitPosition(const nlohmann::json& element) 68*38f85004SShawn McCarney { 69*38f85004SShawn McCarney // Verify element contains an integer 70*38f85004SShawn McCarney if (!element.is_number_integer()) 71*38f85004SShawn McCarney { 72*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an integer"}; 73*38f85004SShawn McCarney } 74*38f85004SShawn McCarney int value = element.get<int>(); 75*38f85004SShawn McCarney if ((value < 0) || (value > 7)) 76*38f85004SShawn McCarney { 77*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a bit position"}; 78*38f85004SShawn McCarney } 79*38f85004SShawn McCarney return static_cast<uint8_t>(value); 80*38f85004SShawn McCarney } 81*38f85004SShawn McCarney 82*38f85004SShawn McCarney /** 83*38f85004SShawn McCarney * Parses a JSON element containing a bit value (0 or 1). 84*38f85004SShawn McCarney * 85*38f85004SShawn McCarney * Returns the corresponding C++ uint8_t value. 86*38f85004SShawn McCarney * 87*38f85004SShawn McCarney * Throws an exception if parsing fails. 88*38f85004SShawn McCarney * 89*38f85004SShawn McCarney * @param element JSON element 90*38f85004SShawn McCarney * @return uint8_t value 91*38f85004SShawn McCarney */ 92*38f85004SShawn McCarney inline uint8_t parseBitValue(const nlohmann::json& element) 93*38f85004SShawn McCarney { 94*38f85004SShawn McCarney // Verify element contains an integer 95*38f85004SShawn McCarney if (!element.is_number_integer()) 96*38f85004SShawn McCarney { 97*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an integer"}; 98*38f85004SShawn McCarney } 99*38f85004SShawn McCarney int value = element.get<int>(); 100*38f85004SShawn McCarney if ((value < 0) || (value > 1)) 101*38f85004SShawn McCarney { 102*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a bit value"}; 103*38f85004SShawn McCarney } 104*38f85004SShawn McCarney return static_cast<uint8_t>(value); 105*38f85004SShawn McCarney } 106*38f85004SShawn McCarney 107*38f85004SShawn McCarney /** 108*38f85004SShawn McCarney * Parses a JSON element containing a boolean. 109*38f85004SShawn McCarney * 110*38f85004SShawn McCarney * Returns the corresponding C++ boolean value. 111*38f85004SShawn McCarney * 112*38f85004SShawn McCarney * Throws an exception if parsing fails. 113*38f85004SShawn McCarney * 114*38f85004SShawn McCarney * @param element JSON element 115*38f85004SShawn McCarney * @return boolean value 116*38f85004SShawn McCarney */ 117*38f85004SShawn McCarney inline bool parseBoolean(const nlohmann::json& element) 118*38f85004SShawn McCarney { 119*38f85004SShawn McCarney // Verify element contains a boolean 120*38f85004SShawn McCarney if (!element.is_boolean()) 121*38f85004SShawn McCarney { 122*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a boolean"}; 123*38f85004SShawn McCarney } 124*38f85004SShawn McCarney return element.get<bool>(); 125*38f85004SShawn McCarney } 126*38f85004SShawn McCarney 127*38f85004SShawn McCarney /** 128*38f85004SShawn McCarney * Parses a JSON element containing a double (floating point number). 129*38f85004SShawn McCarney * 130*38f85004SShawn McCarney * Returns the corresponding C++ double value. 131*38f85004SShawn McCarney * 132*38f85004SShawn McCarney * Throws an exception if parsing fails. 133*38f85004SShawn McCarney * 134*38f85004SShawn McCarney * @param element JSON element 135*38f85004SShawn McCarney * @return double value 136*38f85004SShawn McCarney */ 137*38f85004SShawn McCarney inline double parseDouble(const nlohmann::json& element) 138*38f85004SShawn McCarney { 139*38f85004SShawn McCarney // Verify element contains a number (integer or floating point) 140*38f85004SShawn McCarney if (!element.is_number()) 141*38f85004SShawn McCarney { 142*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a number"}; 143*38f85004SShawn McCarney } 144*38f85004SShawn McCarney return element.get<double>(); 145*38f85004SShawn McCarney } 146*38f85004SShawn McCarney 147*38f85004SShawn McCarney /** 148*38f85004SShawn McCarney * Parses a JSON element containing a byte value expressed as a hexadecimal 149*38f85004SShawn McCarney * string. 150*38f85004SShawn McCarney * 151*38f85004SShawn McCarney * The JSON number data type does not support the hexadecimal format. For this 152*38f85004SShawn McCarney * reason, hexadecimal byte values are stored as strings in the configuration 153*38f85004SShawn McCarney * file. 154*38f85004SShawn McCarney * 155*38f85004SShawn McCarney * Returns the corresponding C++ uint8_t value. 156*38f85004SShawn McCarney * 157*38f85004SShawn McCarney * Throws an exception if parsing fails. 158*38f85004SShawn McCarney * 159*38f85004SShawn McCarney * @param element JSON element 160*38f85004SShawn McCarney * @return uint8_t value 161*38f85004SShawn McCarney */ 162*38f85004SShawn McCarney inline uint8_t parseHexByte(const nlohmann::json& element) 163*38f85004SShawn McCarney { 164*38f85004SShawn McCarney if (!element.is_string()) 165*38f85004SShawn McCarney { 166*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a string"}; 167*38f85004SShawn McCarney } 168*38f85004SShawn McCarney std::string value = element.get<std::string>(); 169*38f85004SShawn McCarney 170*38f85004SShawn McCarney bool isHex = (value.compare(0, 2, "0x") == 0) && (value.size() > 2) && 171*38f85004SShawn McCarney (value.size() < 5) && 172*38f85004SShawn McCarney (value.find_first_not_of("0123456789abcdefABCDEF", 2) == 173*38f85004SShawn McCarney std::string::npos); 174*38f85004SShawn McCarney if (!isHex) 175*38f85004SShawn McCarney { 176*38f85004SShawn McCarney throw std::invalid_argument{"Element is not hexadecimal string"}; 177*38f85004SShawn McCarney } 178*38f85004SShawn McCarney return static_cast<uint8_t>(std::stoul(value, nullptr, 0)); 179*38f85004SShawn McCarney } 180*38f85004SShawn McCarney 181*38f85004SShawn McCarney /** 182*38f85004SShawn McCarney * Parses a JSON element containing an array of byte values expressed as a 183*38f85004SShawn McCarney * hexadecimal strings. 184*38f85004SShawn McCarney * 185*38f85004SShawn McCarney * Returns the corresponding C++ uint8_t values. 186*38f85004SShawn McCarney * 187*38f85004SShawn McCarney * Throws an exception if parsing fails. 188*38f85004SShawn McCarney * 189*38f85004SShawn McCarney * @param element JSON element 190*38f85004SShawn McCarney * @return vector of uint8_t 191*38f85004SShawn McCarney */ 192*38f85004SShawn McCarney std::vector<uint8_t> parseHexByteArray(const nlohmann::json& element); 193*38f85004SShawn McCarney 194*38f85004SShawn McCarney /** 195*38f85004SShawn McCarney * Parses a JSON element containing an 8-bit signed integer. 196*38f85004SShawn McCarney * 197*38f85004SShawn McCarney * Returns the corresponding C++ int8_t value. 198*38f85004SShawn McCarney * 199*38f85004SShawn McCarney * Throws an exception if parsing fails. 200*38f85004SShawn McCarney * 201*38f85004SShawn McCarney * @param element JSON element 202*38f85004SShawn McCarney * @return int8_t value 203*38f85004SShawn McCarney */ 204*38f85004SShawn McCarney inline int8_t parseInt8(const nlohmann::json& element) 205*38f85004SShawn McCarney { 206*38f85004SShawn McCarney // Verify element contains an integer 207*38f85004SShawn McCarney if (!element.is_number_integer()) 208*38f85004SShawn McCarney { 209*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an integer"}; 210*38f85004SShawn McCarney } 211*38f85004SShawn McCarney int value = element.get<int>(); 212*38f85004SShawn McCarney if ((value < INT8_MIN) || (value > INT8_MAX)) 213*38f85004SShawn McCarney { 214*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an 8-bit signed integer"}; 215*38f85004SShawn McCarney } 216*38f85004SShawn McCarney return static_cast<int8_t>(value); 217*38f85004SShawn McCarney } 218*38f85004SShawn McCarney 219*38f85004SShawn McCarney /** 220*38f85004SShawn McCarney * Parses a JSON element containing a string. 221*38f85004SShawn McCarney * 222*38f85004SShawn McCarney * Returns the corresponding C++ string. 223*38f85004SShawn McCarney * 224*38f85004SShawn McCarney * Throws an exception if parsing fails. 225*38f85004SShawn McCarney * 226*38f85004SShawn McCarney * @param element JSON element 227*38f85004SShawn McCarney * @param isEmptyValid indicates whether an empty string value is valid 228*38f85004SShawn McCarney * @return string value 229*38f85004SShawn McCarney */ 230*38f85004SShawn McCarney inline std::string parseString(const nlohmann::json& element, 231*38f85004SShawn McCarney bool isEmptyValid = false) 232*38f85004SShawn McCarney { 233*38f85004SShawn McCarney if (!element.is_string()) 234*38f85004SShawn McCarney { 235*38f85004SShawn McCarney throw std::invalid_argument{"Element is not a string"}; 236*38f85004SShawn McCarney } 237*38f85004SShawn McCarney std::string value = element.get<std::string>(); 238*38f85004SShawn McCarney if (value.empty() && !isEmptyValid) 239*38f85004SShawn McCarney { 240*38f85004SShawn McCarney throw std::invalid_argument{"Element contains an empty string"}; 241*38f85004SShawn McCarney } 242*38f85004SShawn McCarney return value; 243*38f85004SShawn McCarney } 244*38f85004SShawn McCarney 245*38f85004SShawn McCarney /** 246*38f85004SShawn McCarney * Parses a JSON element containing an 8-bit unsigned integer. 247*38f85004SShawn McCarney * 248*38f85004SShawn McCarney * Returns the corresponding C++ uint8_t value. 249*38f85004SShawn McCarney * 250*38f85004SShawn McCarney * Throws an exception if parsing fails. 251*38f85004SShawn McCarney * 252*38f85004SShawn McCarney * @param element JSON element 253*38f85004SShawn McCarney * @return uint8_t value 254*38f85004SShawn McCarney */ 255*38f85004SShawn McCarney inline uint8_t parseUint8(const nlohmann::json& element) 256*38f85004SShawn McCarney { 257*38f85004SShawn McCarney // Verify element contains an integer 258*38f85004SShawn McCarney if (!element.is_number_integer()) 259*38f85004SShawn McCarney { 260*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an integer"}; 261*38f85004SShawn McCarney } 262*38f85004SShawn McCarney int value = element.get<int>(); 263*38f85004SShawn McCarney if ((value < 0) || (value > UINT8_MAX)) 264*38f85004SShawn McCarney { 265*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an 8-bit unsigned integer"}; 266*38f85004SShawn McCarney } 267*38f85004SShawn McCarney return static_cast<uint8_t>(value); 268*38f85004SShawn McCarney } 269*38f85004SShawn McCarney 270*38f85004SShawn McCarney /** 271*38f85004SShawn McCarney * Parses a JSON element containing an unsigned integer. 272*38f85004SShawn McCarney * 273*38f85004SShawn McCarney * Returns the corresponding C++ unsigned int value. 274*38f85004SShawn McCarney * 275*38f85004SShawn McCarney * Throws an exception if parsing fails. 276*38f85004SShawn McCarney * 277*38f85004SShawn McCarney * @param element JSON element 278*38f85004SShawn McCarney * @return unsigned int value 279*38f85004SShawn McCarney */ 280*38f85004SShawn McCarney inline unsigned int parseUnsignedInteger(const nlohmann::json& element) 281*38f85004SShawn McCarney { 282*38f85004SShawn McCarney // Verify element contains an unsigned integer 283*38f85004SShawn McCarney if (!element.is_number_unsigned()) 284*38f85004SShawn McCarney { 285*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an unsigned integer"}; 286*38f85004SShawn McCarney } 287*38f85004SShawn McCarney return element.get<unsigned int>(); 288*38f85004SShawn McCarney } 289*38f85004SShawn McCarney 290*38f85004SShawn McCarney /** 291*38f85004SShawn McCarney * Verifies that the specified JSON element is a JSON array. 292*38f85004SShawn McCarney * 293*38f85004SShawn McCarney * Throws an invalid_argument exception if the element is not an array. 294*38f85004SShawn McCarney * 295*38f85004SShawn McCarney * @param element JSON element 296*38f85004SShawn McCarney */ 297*38f85004SShawn McCarney inline void verifyIsArray(const nlohmann::json& element) 298*38f85004SShawn McCarney { 299*38f85004SShawn McCarney if (!element.is_array()) 300*38f85004SShawn McCarney { 301*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an array"}; 302*38f85004SShawn McCarney } 303*38f85004SShawn McCarney } 304*38f85004SShawn McCarney 305*38f85004SShawn McCarney /** 306*38f85004SShawn McCarney * Verifies that the specified JSON element is a JSON object. 307*38f85004SShawn McCarney * 308*38f85004SShawn McCarney * Throws an invalid_argument exception if the element is not an object. 309*38f85004SShawn McCarney * 310*38f85004SShawn McCarney * @param element JSON element 311*38f85004SShawn McCarney */ 312*38f85004SShawn McCarney inline void verifyIsObject(const nlohmann::json& element) 313*38f85004SShawn McCarney { 314*38f85004SShawn McCarney if (!element.is_object()) 315*38f85004SShawn McCarney { 316*38f85004SShawn McCarney throw std::invalid_argument{"Element is not an object"}; 317*38f85004SShawn McCarney } 318*38f85004SShawn McCarney } 319*38f85004SShawn McCarney 320*38f85004SShawn McCarney /** 321*38f85004SShawn McCarney * Verifies that the specified JSON element contains the expected number of 322*38f85004SShawn McCarney * properties. 323*38f85004SShawn McCarney * 324*38f85004SShawn McCarney * Throws an invalid_argument exception if the element contains a different 325*38f85004SShawn McCarney * number of properties. This indicates the element contains an invalid 326*38f85004SShawn McCarney * property. 327*38f85004SShawn McCarney * 328*38f85004SShawn McCarney * @param element JSON element 329*38f85004SShawn McCarney * @param expectedCount expected number of properties in element 330*38f85004SShawn McCarney */ 331*38f85004SShawn McCarney inline void verifyPropertyCount(const nlohmann::json& element, 332*38f85004SShawn McCarney unsigned int expectedCount) 333*38f85004SShawn McCarney { 334*38f85004SShawn McCarney if (element.size() != expectedCount) 335*38f85004SShawn McCarney { 336*38f85004SShawn McCarney throw std::invalid_argument{"Element contains an invalid property"}; 337*38f85004SShawn McCarney } 338*38f85004SShawn McCarney } 339*38f85004SShawn McCarney 340*38f85004SShawn McCarney } // namespace phosphor::power::json_parser_utils 341