Mercurial > code
comparison cpp/json_util/json_util.hpp @ 650:ff73f2dd1c82
json_util: split and style
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 23 Oct 2018 21:10:00 +0200 |
parents | 3129f59002d2 |
children | 728bdf008ec3 |
comparison
equal
deleted
inserted
replaced
649:3129f59002d2 | 650:ff73f2dd1c82 |
---|---|
23 * \file json_util.hpp | 23 * \file json_util.hpp |
24 * \brief Utilities for JSON. | 24 * \brief Utilities for JSON. |
25 */ | 25 */ |
26 | 26 |
27 #include <cstdint> | 27 #include <cstdint> |
28 #include <limits> | |
29 #include <optional> | 28 #include <optional> |
30 #include <string> | 29 #include <string> |
31 #include <type_traits> | |
32 | 30 |
33 #include <json.hpp> | 31 #include <json.hpp> |
34 | 32 |
35 /** | 33 /** |
36 * \brief Utilities for JSON. | 34 * \brief Utilities for JSON. |
37 */ | 35 */ |
38 namespace json_util { | 36 namespace json_util { |
39 | |
40 /** | |
41 * \cond JSON_UTIL_HIDDEN_SYMBOLS | |
42 */ | |
43 | |
44 namespace detail { | |
45 | |
46 template <typename Int> | |
47 class parser_type_traits_uint : public std::true_type { | |
48 public: | |
49 static std::optional<Int> get(const nlohmann::json& value) noexcept | |
50 { | |
51 if (!value.is_number_unsigned()) | |
52 return std::nullopt; | |
53 | |
54 const auto ret = value.get<std::uint64_t>(); | |
55 | |
56 if (ret > std::numeric_limits<Int>::max()) | |
57 return std::nullopt; | |
58 | |
59 return static_cast<Int>(ret); | |
60 } | |
61 }; | |
62 | |
63 template <typename Int> | |
64 class parser_type_traits_int : public std::true_type { | |
65 public: | |
66 static std::optional<Int> get(const nlohmann::json& value) noexcept | |
67 { | |
68 if (!value.is_number_integer()) | |
69 return std::nullopt; | |
70 | |
71 const auto ret = value.get<std::int64_t>(); | |
72 | |
73 if (ret < std::numeric_limits<Int>::min() || ret > std::numeric_limits<Int>::max()) | |
74 return std::nullopt; | |
75 | |
76 return static_cast<Int>(ret); | |
77 } | |
78 }; | |
79 | |
80 } // !detail | |
81 | |
82 /** | |
83 * \endcond | |
84 */ | |
85 | 37 |
86 /** | 38 /** |
87 * \brief Describe how to convert a JSON value. | 39 * \brief Describe how to convert a JSON value. |
88 * | 40 * |
89 * This class must be specialized for every type you want to convert from JSON | 41 * This class must be specialized for every type you want to convert from JSON |
100 * | 52 * |
101 * This class is already specialized for the given types: | 53 * This class is already specialized for the given types: |
102 * | 54 * |
103 * - bool | 55 * - bool |
104 * - double | 56 * - double |
105 * - std::uint(8, 16, 32, 64) | 57 * - std::uint(8, 16, 32, 64)_t |
106 * - std::string | 58 * - std::string |
107 */ | 59 */ |
108 template <typename T> | 60 template <typename T> |
109 class parser_type_traits : public std::false_type { | 61 struct type_traits; |
110 }; | |
111 | 62 |
112 /** | 63 /** |
113 * \brief Specialization for `bool`. | 64 * \brief Specialization for `bool`. |
114 */ | 65 */ |
115 template <> | 66 template <> |
116 class parser_type_traits<bool> : public std::true_type { | 67 struct type_traits<bool> { |
117 public: | |
118 /** | 68 /** |
119 * Convert the JSON value to bool. | 69 * Convert the JSON value to bool. |
120 * | 70 * |
121 * \param value the value | 71 * \param value the value |
122 * \return the bool or none if not a boolean type | 72 * \return the bool or none if not a boolean type |
123 */ | 73 */ |
124 static std::optional<bool> get(const nlohmann::json& value) noexcept | 74 static auto get(const nlohmann::json& value) noexcept -> std::optional<bool>; |
125 { | |
126 if (!value.is_boolean()) | |
127 return std::nullopt; | |
128 | |
129 return value.get<bool>(); | |
130 } | |
131 }; | 75 }; |
132 | 76 |
133 /** | 77 /** |
134 * \brief Specialization for `double`. | 78 * \brief Specialization for `double`. |
135 */ | 79 */ |
136 template <> | 80 template <> |
137 class parser_type_traits<double> : public std::true_type { | 81 struct type_traits<double> { |
138 public: | |
139 /** | 82 /** |
140 * Convert the JSON value to bool. | 83 * Convert the JSON value to bool. |
141 * | 84 * |
142 * \param value the value | 85 * \param value the value |
143 * \return the double or none if not a double type | 86 * \return the double or none if not a double type |
144 */ | 87 */ |
145 static std::optional<double> get(const nlohmann::json& value) noexcept | 88 static auto get(const nlohmann::json& value) noexcept -> std::optional<double>; |
146 { | |
147 if (!value.is_number_float()) | |
148 return std::nullopt; | |
149 | |
150 return value.get<double>(); | |
151 } | |
152 }; | 89 }; |
153 | 90 |
154 /** | 91 /** |
155 * \brief Specialization for `std::string`. | 92 * \brief Specialization for `std::string`. |
156 */ | 93 */ |
157 template <> | 94 template <> |
158 class parser_type_traits<std::string> : public std::true_type { | 95 struct type_traits<std::string> { |
159 public: | 96 /** |
160 /** | 97 * Convert the JSON value to std::string. |
161 * Convert the JSON value to bool. | |
162 * | 98 * |
163 * \param value the value | 99 * \param value the value |
164 * \return the string or none if not a string type | 100 * \return the string or none if not a string type |
165 */ | 101 */ |
166 static std::optional<std::string> get(const nlohmann::json& value) | 102 static auto get(const nlohmann::json& value) -> std::optional<std::string>; |
167 { | |
168 if (!value.is_string()) | |
169 return std::nullopt; | |
170 | |
171 return value.get<std::string>(); | |
172 } | |
173 }; | 103 }; |
174 | 104 |
175 /** | 105 /** |
176 * \brief Specialization for `std::int8_t`. | 106 * \brief Specialization for `std::int8_t`. |
177 */ | 107 */ |
178 template <> | 108 template <> |
179 class parser_type_traits<std::int8_t> : public detail::parser_type_traits_int<std::int8_t> { | 109 struct type_traits<std::int8_t> { |
110 /** | |
111 * Convert the JSON value to std::int8_t. | |
112 * | |
113 * \param value the value | |
114 * \return the value or none if value does not fit between the range | |
115 */ | |
116 static auto get(const nlohmann::json& value) -> std::optional<std::int8_t>; | |
180 }; | 117 }; |
181 | 118 |
182 /** | 119 /** |
183 * \brief Specialization for `std::int16_t`. | 120 * \brief Specialization for `std::int16_t`. |
184 */ | 121 */ |
185 template <> | 122 template <> |
186 class parser_type_traits<std::int16_t> : public detail::parser_type_traits_int<std::int16_t> { | 123 struct type_traits<std::int16_t> { |
124 /** | |
125 * Convert the JSON value to std::int16_t. | |
126 * | |
127 * \param value the value | |
128 * \return the value or none if value does not fit between the range | |
129 */ | |
130 static auto get(const nlohmann::json& value) -> std::optional<std::int16_t>; | |
187 }; | 131 }; |
188 | 132 |
189 /** | 133 /** |
190 * \brief Specialization for `std::int32_t`. | 134 * \brief Specialization for `std::int32_t`. |
191 */ | 135 */ |
192 template <> | 136 template <> |
193 class parser_type_traits<std::int32_t> : public detail::parser_type_traits_int<std::int32_t> { | 137 struct type_traits<std::int32_t> { |
138 /** | |
139 * Convert the JSON value to std::int32_t. | |
140 * | |
141 * \param value the value | |
142 * \return the value or none if value does not fit between the range | |
143 */ | |
144 static auto get(const nlohmann::json& value) -> std::optional<std::int32_t>; | |
194 }; | 145 }; |
195 | 146 |
196 /** | 147 /** |
197 * \brief Specialization for `std::int64_t`. | 148 * \brief Specialization for `std::int64_t`. |
198 */ | 149 */ |
199 template <> | 150 template <> |
200 class parser_type_traits<std::int64_t> : public std::true_type { | 151 struct type_traits<std::int64_t> { |
201 public: | |
202 /** | 152 /** |
203 * Convert the JSON value to std::int64_t. | 153 * Convert the JSON value to std::int64_t. |
204 * | 154 * |
205 * \param value the value | 155 * \param value the value |
206 * \return the int or none if not a int type | 156 * \return the int or none if not a int type |
207 */ | 157 */ |
208 static std::optional<std::int64_t> get(const nlohmann::json& value) noexcept | 158 static auto get(const nlohmann::json& value) noexcept -> std::optional<std::int64_t>; |
209 { | 159 }; |
210 if (!value.is_number_integer()) | 160 |
211 return std::nullopt; | 161 /** |
212 | 162 * \brief Specialization for `std::uint8_t`. |
213 return value.get<std::int64_t>(); | 163 */ |
214 } | 164 template <> |
215 }; | 165 struct type_traits<std::uint8_t> { |
216 | 166 /** |
217 /** | 167 * Convert the JSON value to std::uint8_t. |
218 * \brief Specialization for `std::int8_t`. | 168 * |
219 */ | 169 * \param value the value |
220 template <> | 170 * \return the value or none if value does not fit between the range |
221 class parser_type_traits<std::uint8_t> : public detail::parser_type_traits_uint<std::uint8_t> { | 171 */ |
222 }; | 172 static auto get(const nlohmann::json& value) -> std::optional<std::uint8_t>; |
223 | 173 }; |
224 /** | 174 |
225 * \brief Specialization for `std::int16_t`. | 175 /** |
226 */ | 176 * \brief Specialization for `std::uint16_t`. |
227 template <> | 177 */ |
228 class parser_type_traits<std::uint16_t> : public detail::parser_type_traits_uint<std::uint16_t> { | 178 template <> |
179 struct type_traits<std::uint16_t> { | |
180 /** | |
181 * Convert the JSON value to std::uint16_t. | |
182 * | |
183 * \param value the value | |
184 * \return the value or none if value does not fit between the range | |
185 */ | |
186 static auto get(const nlohmann::json& value) -> std::optional<std::uint16_t>; | |
229 }; | 187 }; |
230 | 188 |
231 /** | 189 /** |
232 * \brief Specialization for `std::int32_t`. | 190 * \brief Specialization for `std::int32_t`. |
233 */ | 191 */ |
234 template <> | 192 template <> |
235 class parser_type_traits<std::uint32_t> : public detail::parser_type_traits_uint<std::uint32_t> { | 193 struct type_traits<std::uint32_t> { |
236 }; | 194 /** |
237 | 195 * Convert the JSON value to std::uint32_t. |
238 /** | 196 * |
239 * \brief Specialization for `std::int64_t`. | 197 * \param value the value |
240 */ | 198 * \return the value or none if value does not fit between the range |
241 template <> | 199 */ |
242 class parser_type_traits<std::uint64_t> : public std::true_type { | 200 static auto get(const nlohmann::json& value) -> std::optional<std::uint32_t>; |
243 public: | 201 }; |
202 | |
203 /** | |
204 * \brief Specialization for `std::uint64_t`. | |
205 */ | |
206 template <> | |
207 struct type_traits<std::uint64_t> { | |
244 /** | 208 /** |
245 * Convert the JSON value to std::uint64_t. | 209 * Convert the JSON value to std::uint64_t. |
246 * | 210 * |
247 * \param value the value | 211 * \param value the value |
248 * \return the int or none if not a int type | 212 * \return the int or none if not a int type |
249 */ | 213 */ |
250 static std::optional<std::uint64_t> get(const nlohmann::json& value) noexcept | 214 static auto get(const nlohmann::json& value) noexcept -> std::optional<std::uint64_t>; |
251 { | |
252 if (!value.is_number_unsigned()) | |
253 return std::nullopt; | |
254 | |
255 return value.get<std::uint64_t>(); | |
256 } | |
257 }; | 215 }; |
258 | 216 |
259 /** | 217 /** |
260 * \brief Convenient JSON object parser | 218 * \brief Convenient JSON object parser |
261 * | 219 * |
262 * This class helps destructuring insecure JSON input by returning optional | 220 * This class helps destructuring insecure JSON input by returning optional |
263 * values if they are not present or invalid. | 221 * values if they are not present or invalid. |
264 */ | 222 */ |
265 class document : public nlohmann::json { | 223 class deserializer : public nlohmann::json { |
266 public: | 224 public: |
267 /** | 225 /** |
268 * Inherited constructor. | 226 * Inherited constructor. |
269 */ | 227 */ |
270 using nlohmann::json::json; | 228 using nlohmann::json::json; |
274 * | 232 * |
275 * \param key the property key | 233 * \param key the property key |
276 * \return the value or std::nullopt if not found or not convertible | 234 * \return the value or std::nullopt if not found or not convertible |
277 */ | 235 */ |
278 template <typename Type> | 236 template <typename Type> |
279 inline std::optional<Type> get(const std::string& key) const noexcept | 237 auto get(const std::string& key) const noexcept -> std::optional<Type> |
280 { | 238 { |
281 static_assert(parser_type_traits<Type>::value, "type not supported"); | |
282 | |
283 const auto it = find(key); | 239 const auto it = find(key); |
284 | 240 |
285 if (it == end()) | 241 if (it == end()) |
286 return std::nullopt; | 242 return std::nullopt; |
287 | 243 |
288 return parser_type_traits<Type>::get(*it); | 244 return type_traits<Type>::get(*it); |
289 } | 245 } |
290 | 246 |
291 /** | 247 /** |
292 * Get an optional value from the document object. | 248 * Get an optional value from the document object. |
293 * | 249 * |
297 * \param key the property key | 253 * \param key the property key |
298 * \param def the default value if property is undefined | 254 * \param def the default value if property is undefined |
299 * \return the value, std::nullopt or def | 255 * \return the value, std::nullopt or def |
300 */ | 256 */ |
301 template <typename Type, typename DefaultValue> | 257 template <typename Type, typename DefaultValue> |
302 inline std::optional<Type> optional(const std::string& key, DefaultValue&& def) const noexcept | 258 auto optional(const std::string& key, DefaultValue&& def) const noexcept -> std::optional<Type> |
303 { | 259 { |
304 static_assert(parser_type_traits<Type>::value, "type not supported"); | |
305 | |
306 const auto it = find(key); | 260 const auto it = find(key); |
307 | 261 |
308 if (it == end()) | 262 if (it == end()) |
309 return std::optional<Type>(std::forward<DefaultValue>(def)); | 263 return std::optional<Type>(std::forward<DefaultValue>(def)); |
310 | 264 |
311 return parser_type_traits<Type>::get(*it); | 265 return type_traits<Type>::get(*it); |
312 } | 266 } |
313 }; | 267 }; |
314 | 268 |
315 /** | 269 /** |
316 * Print the value as human readable. | 270 * Print the value as human readable. |
318 * \note This only works on flat objects. | 272 * \note This only works on flat objects. |
319 * \param value the value | 273 * \param value the value |
320 * \param indent the optional indent for objects/arrays | 274 * \param indent the optional indent for objects/arrays |
321 * \return the string | 275 * \return the string |
322 */ | 276 */ |
323 inline std::string pretty(const nlohmann::json& value, int indent = 4) | 277 auto pretty(const nlohmann::json& value, int indent = 4) -> std::string; |
324 { | |
325 switch (value.type()) { | |
326 case nlohmann::json::value_t::null: | |
327 return "null"; | |
328 case nlohmann::json::value_t::string: | |
329 return value.get<std::string>(); | |
330 case nlohmann::json::value_t::boolean: | |
331 return value.get<bool>() ? "true" : "false"; | |
332 case nlohmann::json::value_t::number_integer: | |
333 return std::to_string(value.get<std::int64_t>()); | |
334 case nlohmann::json::value_t::number_unsigned: | |
335 return std::to_string(value.get<std::uint64_t>()); | |
336 case nlohmann::json::value_t::number_float: | |
337 return std::to_string(value.get<double>()); | |
338 default: | |
339 return value.dump(indent); | |
340 } | |
341 } | |
342 | 278 |
343 } // !json_util | 279 } // !json_util |
344 | 280 |
345 #endif // !JSON_UTIL_HPP | 281 #endif // !JSON_UTIL_HPP |