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