xref: /openbmc/phosphor-power/json_parser_utils.hpp (revision 38f8500414fe5c1be6f5159c563937289fe557c2)
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