From c23093e0f0a28bcc6a05fe06953d70be3f96cee3 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Wed, 28 Sep 2022 00:24:48 -0300 Subject: [PATCH] [libsolutil] Add new JSON helper functions. --- libsolutil/JSON.h | 65 ++++++++++++++++++++ test/libsolutil/JSON.cpp | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/libsolutil/JSON.h b/libsolutil/JSON.h index 43dddfd4a..12c450a32 100644 --- a/libsolutil/JSON.h +++ b/libsolutil/JSON.h @@ -67,4 +67,69 @@ std::string jsonPrint(Json::Value const& _input, JsonFormat const& _format); /// \return \c true if the document was successfully parsed, \c false if an error occurred. bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); +namespace detail +{ + +template +struct helper; + +#define DEFINE_JSON_CONVERSION_HELPERS(TYPE, CHECK_TYPE_MEMBER, CONVERT_TYPE_MEMBER) \ + template<> \ + struct helper \ + { \ + static bool isOfType(Json::Value const& _input) \ + { \ + return _input.CHECK_TYPE_MEMBER(); \ + } \ + static TYPE get(Json::Value const& _input) \ + { \ + return _input.CONVERT_TYPE_MEMBER(); \ + } \ + static TYPE getOrDefault(Json::Value const& _input, TYPE _default = {}) \ + { \ + TYPE result = _default; \ + if (helper::isOfType(_input)) \ + result = _input.CONVERT_TYPE_MEMBER(); \ + return result; \ + } \ + }; + +DEFINE_JSON_CONVERSION_HELPERS(float, isDouble, asFloat) +DEFINE_JSON_CONVERSION_HELPERS(double, isDouble, asDouble) +DEFINE_JSON_CONVERSION_HELPERS(std::string, isString, asString) +DEFINE_JSON_CONVERSION_HELPERS(Json::Int, isInt, asInt) +DEFINE_JSON_CONVERSION_HELPERS(Json::Int64, isInt64, asInt64) +DEFINE_JSON_CONVERSION_HELPERS(Json::UInt, isUInt, asUInt) +DEFINE_JSON_CONVERSION_HELPERS(Json::UInt64, isUInt64, asUInt64) + +#undef DEFINE_JSON_CONVERSION_HELPERS + +} // namespace detail + +template +bool isOfType(Json::Value const& _input) +{ + return detail::helper::isOfType(_input); } + +template +bool isOfTypeIfExists(Json::Value const& _input, std::string const& _name) +{ + if (_input.isMember(_name)) + return isOfType(_input[_name]); + return true; +} + +template +T get(Json::Value const& _input) +{ + return detail::helper::get(_input); +} + +template +T getOrDefault(Json::Value const& _input, T _default = {}) +{ + return detail::helper::getOrDefault(_input, _default); +} + +} // namespace solidity::util diff --git a/test/libsolutil/JSON.cpp b/test/libsolutil/JSON.cpp index 1b0ed9388..3679fbe7c 100644 --- a/test/libsolutil/JSON.cpp +++ b/test/libsolutil/JSON.cpp @@ -184,6 +184,131 @@ BOOST_AUTO_TEST_CASE(parse_json_strict) BOOST_CHECK(json[0] == "😊"); } +BOOST_AUTO_TEST_CASE(json_isOfType) +{ + Json::Value json; + + json["float"] = 3.1f; + json["double"] = 3.1; + json["int"] = 2; + json["int64"] = Json::Int64{0x4000000000000000}; + json["string"] = "Hello World!"; + + BOOST_CHECK(isOfType(json["float"])); + BOOST_CHECK(isOfType(json["double"])); + BOOST_CHECK(isOfType(json["int"])); + BOOST_CHECK(isOfType(json["int"])); + BOOST_CHECK(isOfType(json["int"])); + BOOST_CHECK(isOfType(json["int"])); + BOOST_CHECK(isOfType(json["int64"])); + BOOST_CHECK(isOfType(json["int64"])); + BOOST_CHECK(isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["int64"])); + BOOST_CHECK(!isOfType(json["double"])); + BOOST_CHECK(!isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["string"])); + BOOST_CHECK(!isOfType(json["string"])); +} + +BOOST_AUTO_TEST_CASE(json_isisOfTypeIfExists) +{ + Json::Value json; + + json["float"] = 3.1f; + json["double"] = 3.1; + json["int"] = 2; + json["int64"] = Json::Int64{0x4000000000000000}; + json["string"] = "Hello World!"; + + BOOST_CHECK(isOfTypeIfExists(json, "float")); + BOOST_CHECK(isOfTypeIfExists(json, "double")); + BOOST_CHECK(isOfTypeIfExists(json, "int")); + BOOST_CHECK(isOfTypeIfExists(json, "int")); + BOOST_CHECK(isOfTypeIfExists(json, "int")); + BOOST_CHECK(isOfTypeIfExists(json, "int")); + BOOST_CHECK(isOfTypeIfExists(json, "int64")); + BOOST_CHECK(isOfTypeIfExists(json, "int64")); + BOOST_CHECK(isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "int64")); + BOOST_CHECK(!isOfTypeIfExists(json, "double")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(!isOfTypeIfExists(json, "string")); + BOOST_CHECK(isOfTypeIfExists(json, "NOT_EXISTING")); +} + +BOOST_AUTO_TEST_CASE(json_getOrDefault) +{ + Json::Value json; + + json["float"] = 3.1f; + json["double"] = 3.1; + json["int"] = 2; + json["int64"] = Json::Int64{0x4000000000000000}; + json["uint64"] = Json::UInt64{0x5000000000000000}; + json["string"] = "Hello World!"; + + BOOST_CHECK(getOrDefault(json["float"]) == 3.1f); + BOOST_CHECK(getOrDefault(json["float"], -1.1f) == 3.1f); + BOOST_CHECK(getOrDefault(json["no_float"], -1.1f) == -1.1f); + BOOST_CHECK(getOrDefault(json["double"]) == 3.1); + BOOST_CHECK(getOrDefault(json["double"], -1) == 3.1); + BOOST_CHECK(getOrDefault(json["no_double"], -1.1) == -1.1); + BOOST_CHECK(getOrDefault(json["int"]) == 2); + BOOST_CHECK(getOrDefault(json["int"], -1) == 2); + BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); + BOOST_CHECK(getOrDefault(json["int"]) == 2); + BOOST_CHECK(getOrDefault(json["int"], -1) == 2); + BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); + BOOST_CHECK(getOrDefault(json["int"]) == 2); + BOOST_CHECK(getOrDefault(json["int"], 1) == 2); + BOOST_CHECK(getOrDefault(json["no_int"], 1) == 1); + BOOST_CHECK(getOrDefault(json["int"]) == 2); + BOOST_CHECK(getOrDefault(json["int"], -1) == 2); + BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); + BOOST_CHECK(getOrDefault(json["int64"]) == 0x4000000000000000); + BOOST_CHECK(getOrDefault(json["int64"], -1) == 0x4000000000000000); + BOOST_CHECK(getOrDefault(json["no_int64"], -1) == -1); + BOOST_CHECK(getOrDefault(json["int64"]) == 0x4000000000000000); + BOOST_CHECK(getOrDefault(json["int64"], 1) == 0x4000000000000000); + BOOST_CHECK(getOrDefault(json["no_int64"], 1) == 1); + BOOST_CHECK(getOrDefault(json["uint64"]) == 0x5000000000000000); + BOOST_CHECK(getOrDefault(json["uint64"], 1) == 0x5000000000000000); + BOOST_CHECK(getOrDefault(json["no_uint64"], 1) == 1); + BOOST_CHECK(getOrDefault(json["string"], "ERROR") == "Hello World!"); + BOOST_CHECK(getOrDefault(json["no_string"]).empty()); + BOOST_CHECK(getOrDefault(json["no_string"], "ERROR") == "ERROR"); +} + +BOOST_AUTO_TEST_CASE(json_get) +{ + Json::Value json; + + json["float"] = 3.1f; + json["double"] = 3.1; + json["int"] = 2; + json["int64"] = Json::Int64{0x4000000000000000}; + json["uint64"] = Json::UInt64{0x5000000000000000}; + json["string"] = "Hello World!"; + + BOOST_CHECK(get(json["float"]) == 3.1f); + BOOST_CHECK(get(json["double"]) == 3.1); + BOOST_CHECK(get(json["int"]) == 2); + BOOST_CHECK(get(json["int"]) == 2); + BOOST_CHECK(get(json["int"]) == 2); + BOOST_CHECK(get(json["int"]) == 2); + BOOST_CHECK(get(json["int64"]) == 0x4000000000000000); + BOOST_CHECK(get(json["int64"]) == 0x4000000000000000); + BOOST_CHECK(get(json["uint64"]) == 0x5000000000000000); + BOOST_CHECK(get(json["string"]) == "Hello World!"); +} + BOOST_AUTO_TEST_SUITE_END() }