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 <map>
22 #include <stdexcept>
23 #include <string>
24 #include <vector>
25
26 /**
27 * @namespace json_parser_utils
28 *
29 * Contains utility functions for parsing JSON data.
30 *
31 * ## Variables
32 * The parsing functions support optional usage of variables. JSON string values
33 * can contain one or more variables. A variable is specified using the format
34 * `${variable_name}`. A variables map is specified to parsing functions that
35 * provides the variable values. Any variable in a JSON string value will be
36 * replaced by the variable value.
37 *
38 * Variables can only appear in a JSON string value. The parsing functions for
39 * other data types, such as integer and double, support a string value if it
40 * contains a variable. After variable expansion, the string is converted to the
41 * expected data type.
42 */
43 namespace phosphor::power::json_parser_utils
44 {
45
46 /**
47 * Empty variables map used as a default value for parsing functions.
48 */
49 extern const std::map<std::string, std::string> NO_VARIABLES;
50
51 /**
52 * Returns the specified property of the specified JSON element.
53 *
54 * Throws an invalid_argument exception if the property does not exist.
55 *
56 * @param element JSON element
57 * @param property property name
58 */
59 #pragma GCC diagnostic push
60 #if __GNUC__ >= 13
61 #pragma GCC diagnostic ignored "-Wdangling-reference"
62 #endif
getRequiredProperty(const nlohmann::json & element,const std::string & property)63 inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element,
64 const std::string& property)
65 {
66 auto it = element.find(property);
67 if (it == element.end())
68 {
69 throw std::invalid_argument{"Required property missing: " + property};
70 }
71 return *it;
72 }
73 #pragma GCC diagnostic pop
74
75 /**
76 * Parses a JSON element containing a bit position (from 0-7).
77 *
78 * Returns the corresponding C++ uint8_t value.
79 *
80 * Throws an exception if parsing fails.
81 *
82 * @param element JSON element
83 * @param variables variables map used to expand variables in element value
84 * @return uint8_t value
85 */
86 uint8_t parseBitPosition(
87 const nlohmann::json& element,
88 const std::map<std::string, std::string>& variables = NO_VARIABLES);
89
90 /**
91 * Parses a JSON element containing a bit value (0 or 1).
92 *
93 * Returns the corresponding C++ uint8_t value.
94 *
95 * Throws an exception if parsing fails.
96 *
97 * @param element JSON element
98 * @param variables variables map used to expand variables in element value
99 * @return uint8_t value
100 */
101 uint8_t parseBitValue(
102 const nlohmann::json& element,
103 const std::map<std::string, std::string>& variables = NO_VARIABLES);
104
105 /**
106 * Parses a JSON element containing a boolean.
107 *
108 * Returns the corresponding C++ boolean value.
109 *
110 * Throws an exception if parsing fails.
111 *
112 * @param element JSON element
113 * @param variables variables map used to expand variables in element value
114 * @return boolean value
115 */
116 bool parseBoolean(
117 const nlohmann::json& element,
118 const std::map<std::string, std::string>& variables = NO_VARIABLES);
119
120 /**
121 * Parses a JSON element containing a double (floating point number).
122 *
123 * Returns the corresponding C++ double value.
124 *
125 * Throws an exception if parsing fails.
126 *
127 * @param element JSON element
128 * @param variables variables map used to expand variables in element value
129 * @return double value
130 */
131 double parseDouble(
132 const nlohmann::json& element,
133 const std::map<std::string, std::string>& variables = NO_VARIABLES);
134
135 /**
136 * Parses a JSON element containing a byte value expressed as a hexadecimal
137 * string.
138 *
139 * The JSON number data type does not support the hexadecimal format. For this
140 * reason, a hexadecimal byte value is stored in a JSON string.
141 *
142 * Returns the corresponding C++ uint8_t value.
143 *
144 * Throws an exception if parsing fails.
145 *
146 * @param element JSON element
147 * @param variables variables map used to expand variables in element value
148 * @return uint8_t value
149 */
150 uint8_t parseHexByte(
151 const nlohmann::json& element,
152 const std::map<std::string, std::string>& variables = NO_VARIABLES);
153
154 /**
155 * Parses a JSON element containing an array of byte values expressed as a
156 * hexadecimal strings.
157 *
158 * Returns the corresponding C++ uint8_t values.
159 *
160 * Throws an exception if parsing fails.
161 *
162 * @param element JSON element
163 * @param variables variables map used to expand variables in element value
164 * @return vector of uint8_t
165 */
166 std::vector<uint8_t> parseHexByteArray(
167 const nlohmann::json& element,
168 const std::map<std::string, std::string>& variables = NO_VARIABLES);
169
170 /**
171 * Parses a JSON element containing an 8-bit signed integer.
172 *
173 * Returns the corresponding C++ int8_t value.
174 *
175 * Throws an exception if parsing fails.
176 *
177 * @param element JSON element
178 * @param variables variables map used to expand variables in element value
179 * @return int8_t value
180 */
181 int8_t parseInt8(
182 const nlohmann::json& element,
183 const std::map<std::string, std::string>& variables = NO_VARIABLES);
184
185 /**
186 * Parses a JSON element containing an integer.
187 *
188 * Returns the corresponding C++ int value.
189 *
190 * Throws an exception if parsing fails.
191 *
192 * @param element JSON element
193 * @param variables variables map used to expand variables in element value
194 * @return integer value
195 */
196 int parseInteger(
197 const nlohmann::json& element,
198 const std::map<std::string, std::string>& variables = NO_VARIABLES);
199
200 /**
201 * Parses a JSON element containing a string.
202 *
203 * Returns the corresponding C++ string.
204 *
205 * Throws an exception if parsing fails.
206 *
207 * @param element JSON element
208 * @param isEmptyValid indicates whether an empty string value is valid
209 * @param variables variables map used to expand variables in element value
210 * @return string value
211 */
212 std::string parseString(
213 const nlohmann::json& element, bool isEmptyValid = false,
214 const std::map<std::string, std::string>& variables = NO_VARIABLES);
215
216 /**
217 * Parses a JSON element containing an 8-bit unsigned integer.
218 *
219 * Returns the corresponding C++ uint8_t value.
220 *
221 * Throws an exception if parsing fails.
222 *
223 * @param element JSON element
224 * @param variables variables map used to expand variables in element value
225 * @return uint8_t value
226 */
227 uint8_t parseUint8(
228 const nlohmann::json& element,
229 const std::map<std::string, std::string>& variables = NO_VARIABLES);
230
231 /**
232 * Parses a JSON element containing a 16-bit unsigned integer.
233 *
234 * Returns the corresponding C++ uint16_t value.
235 *
236 * Throws an exception if parsing fails.
237 *
238 * @param element JSON element
239 * @param variables variables map used to expand variables in element value
240 * @return uint16_t value
241 */
242 uint16_t parseUint16(
243 const nlohmann::json& element,
244 const std::map<std::string, std::string>& variables = NO_VARIABLES);
245
246 /**
247 * Parses a JSON element containing an unsigned integer.
248 *
249 * Returns the corresponding C++ unsigned int value.
250 *
251 * Throws an exception if parsing fails.
252 *
253 * @param element JSON element
254 * @param variables variables map used to expand variables in element value
255 * @return unsigned int value
256 */
257 unsigned int parseUnsignedInteger(
258 const nlohmann::json& element,
259 const std::map<std::string, std::string>& variables = NO_VARIABLES);
260
261 /**
262 * Verifies that the specified JSON element is a JSON array.
263 *
264 * Throws an invalid_argument exception if the element is not an array.
265 *
266 * @param element JSON element
267 */
verifyIsArray(const nlohmann::json & element)268 inline void verifyIsArray(const nlohmann::json& element)
269 {
270 if (!element.is_array())
271 {
272 throw std::invalid_argument{"Element is not an array"};
273 }
274 }
275
276 /**
277 * Verifies that the specified JSON element is a JSON object.
278 *
279 * Throws an invalid_argument exception if the element is not an object.
280 *
281 * @param element JSON element
282 */
verifyIsObject(const nlohmann::json & element)283 inline void verifyIsObject(const nlohmann::json& element)
284 {
285 if (!element.is_object())
286 {
287 throw std::invalid_argument{"Element is not an object"};
288 }
289 }
290
291 /**
292 * Verifies that the specified JSON element contains the expected number of
293 * properties.
294 *
295 * Throws an invalid_argument exception if the element contains a different
296 * number of properties. This indicates the element contains an invalid
297 * property.
298 *
299 * @param element JSON element
300 * @param expectedCount expected number of properties in element
301 */
verifyPropertyCount(const nlohmann::json & element,unsigned int expectedCount)302 inline void verifyPropertyCount(const nlohmann::json& element,
303 unsigned int expectedCount)
304 {
305 if (element.size() != expectedCount)
306 {
307 throw std::invalid_argument{"Element contains an invalid property"};
308 }
309 }
310
311 namespace internal
312 {
313
314 /**
315 * Expands any variables that appear in the specified string value.
316 *
317 * Does nothing if the variables map is empty or the value contains no
318 * variables.
319 *
320 * Throws an invalid_argument exception if a variable occurs in the value that
321 * does not exist in the variables map.
322 *
323 * @param value string value in which to perform variable expansion
324 * @param variables variables map containing variable values
325 */
326 void expandVariables(std::string& value,
327 const std::map<std::string, std::string>& variables);
328
329 } // namespace internal
330
331 } // namespace phosphor::power::json_parser_utils
332