Mercurial > code
view cpp/json_util/json_util.hpp @ 648:5bd9424a523a
misc: extreme cleanup
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 04 Oct 2018 21:17:55 +0200 |
parents | 0a947aec477c |
children | 3129f59002d2 |
line wrap: on
line source
/* * json_util.hpp -- utilities for JSON * * Copyright (c) 2018 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef JSON_UTIL_HPP #define JSON_UTIL_HPP /** * \file json_util.hpp * \brief Utilities for JSON. */ #include <cstdint> #include <limits> #include <string> #include <type_traits> #include <boost/optional.hpp> #include <json.hpp> /** * \brief Utilities for JSON. */ namespace json_util { /** * \cond JSON_UTIL_HIDDEN_SYMBOLS */ namespace detail { template <typename Int> class parser_type_traits_uint : public std::true_type { public: static boost::optional<Int> get(const nlohmann::json& value) noexcept { if (!value.is_number_unsigned()) return boost::none; const auto ret = value.get<std::uint64_t>(); if (ret > std::numeric_limits<Int>::max()) return boost::none; return static_cast<Int>(ret); } }; template <typename Int> class parser_type_traits_int : public std::true_type { public: static boost::optional<Int> get(const nlohmann::json& value) noexcept { if (!value.is_number_integer()) return boost::none; const auto ret = value.get<std::int64_t>(); if (ret < std::numeric_limits<Int>::min() || ret > std::numeric_limits<Int>::max()) return boost::none; return static_cast<Int>(ret); } }; } // !detail /** * \endcond */ /** * \brief Describe how to convert a JSON value. * * This class must be specialized for every type you want to convert from JSON * to its native type. * * You only need to implement the get function with the following signature: * * ```cpp * static boost::optional<T> get(const nlohmann::json& value); * ``` * * The implementation should not throw an exception but return a null optional * instead. * * This class is already specialized for the given types: * * - bool * - double * - std::uint(8, 16, 32, 64) * - std::string */ template <typename T> class parser_type_traits : public std::false_type { }; /** * \brief Specialization for `bool`. */ template <> class parser_type_traits<bool> : public std::true_type { public: /** * Convert the JSON value to bool. * * \param value the value * \return the bool or none if not a boolean type */ static boost::optional<bool> get(const nlohmann::json& value) noexcept { if (!value.is_boolean()) return boost::none; return value.get<bool>(); } }; /** * \brief Specialization for `double`. */ template <> class parser_type_traits<double> : public std::true_type { public: /** * Convert the JSON value to bool. * * \param value the value * \return the double or none if not a double type */ static boost::optional<double> get(const nlohmann::json& value) noexcept { if (!value.is_number_float()) return boost::none; return value.get<double>(); } }; /** * \brief Specialization for `std::string`. */ template <> class parser_type_traits<std::string> : public std::true_type { public: /** * Convert the JSON value to bool. * * \param value the value * \return the string or none if not a string type */ static boost::optional<std::string> get(const nlohmann::json& value) { if (!value.is_string()) return boost::none; return value.get<std::string>(); } }; /** * \brief Specialization for `std::int8_t`. */ template <> class parser_type_traits<std::int8_t> : public detail::parser_type_traits_int<std::int8_t> { }; /** * \brief Specialization for `std::int16_t`. */ template <> class parser_type_traits<std::int16_t> : public detail::parser_type_traits_int<std::int16_t> { }; /** * \brief Specialization for `std::int32_t`. */ template <> class parser_type_traits<std::int32_t> : public detail::parser_type_traits_int<std::int32_t> { }; /** * \brief Specialization for `std::int64_t`. */ template <> class parser_type_traits<std::int64_t> : public std::true_type { public: /** * Convert the JSON value to std::int64_t. * * \param value the value * \return the int or none if not a int type */ static boost::optional<std::int64_t> get(const nlohmann::json& value) noexcept { if (!value.is_number_integer()) return boost::none; return value.get<std::int64_t>(); } }; /** * \brief Specialization for `std::int8_t`. */ template <> class parser_type_traits<std::uint8_t> : public detail::parser_type_traits_uint<std::uint8_t> { }; /** * \brief Specialization for `std::int16_t`. */ template <> class parser_type_traits<std::uint16_t> : public detail::parser_type_traits_uint<std::uint16_t> { }; /** * \brief Specialization for `std::int32_t`. */ template <> class parser_type_traits<std::uint32_t> : public detail::parser_type_traits_uint<std::uint32_t> { }; /** * \brief Specialization for `std::int64_t`. */ template <> class parser_type_traits<std::uint64_t> : public std::true_type { public: /** * Convert the JSON value to std::uint64_t. * * \param value the value * \return the int or none if not a int type */ static boost::optional<std::uint64_t> get(const nlohmann::json& value) noexcept { if (!value.is_number_unsigned()) return boost::none; return value.get<std::uint64_t>(); } }; /** * \brief Convenient JSON object parser * * This class helps destructuring insecure JSON input by returning optional * values if they are not present or invalid. */ class document : public nlohmann::json { public: /** * Inherited constructor. */ using nlohmann::json::json; /** * Get a value from the document object. * * \param key the property key * \return the value or boost::none if not found or not convertible */ template <typename Type> inline boost::optional<Type> get(const std::string& key) const noexcept { static_assert(parser_type_traits<Type>::value, "type not supported"); const auto it = find(key); if (it == end()) return boost::none; return parser_type_traits<Type>::get(*it); } /** * Get an optional value from the document object. * * If the value is undefined, the default value is returned. Otherwise, if * the value is not in the given type, boost::none is returned. * * \param key the property key * \param def the default value if property is undefined * \return the value, boost::none or def */ template <typename Type, typename DefaultValue> inline boost::optional<Type> optional(const std::string& key, DefaultValue&& def) const noexcept { static_assert(parser_type_traits<Type>::value, "type not supported"); const auto it = find(key); if (it == end()) return boost::optional<Type>(std::forward<DefaultValue>(def)); return parser_type_traits<Type>::get(*it); } }; /** * Print the value as human readable. * * \note This only works on flat objects. * \param value the value * \param indent the optional indent for objects/arrays * \return the string */ inline std::string pretty(const nlohmann::json& value, int indent = 4) { switch (value.type()) { case nlohmann::json::value_t::null: return "null"; case nlohmann::json::value_t::string: return value.get<std::string>(); case nlohmann::json::value_t::boolean: return value.get<bool>() ? "true" : "false"; case nlohmann::json::value_t::number_integer: return std::to_string(value.get<std::int64_t>()); case nlohmann::json::value_t::number_unsigned: return std::to_string(value.get<std::uint64_t>()); case nlohmann::json::value_t::number_float: return std::to_string(value.get<double>()); default: return value.dump(indent); } } } // !json_util #endif // !JSON_UTIL_HPP