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