View in context in the libbitcoin Slack #general mirror.
boost::json we get the streaming and non-streaming parse/serialize for free. This is what that looks like, using a couple of our libbitcoin-system macros to reduce the syntactic overhead:
namespace libbitcoin {
namespace network {
namespace json {
DECLARE_JSON_TAG_INVOKE(version);
DECLARE_JSON_TAG_INVOKE(value_t);
DECLARE_JSON_TAG_INVOKE(identity_t);
DECLARE_JSON_TAG_INVOKE(request_t);
DECLARE_JSON_TAG_INVOKE(response_t);
} // namespace json
} // namespace network
} // namespace libbitcoinnamespace libbitcoin {
namespace network {
namespace json {
// version
// ----------------------------------------------------------------------------
DEFINE_JSON_FROM_TAG(version)
{
switch (instance)
{
case version::v1: value = "1.0"; break;
case version::v2: value = "2.0"; break;
default: value = {}; break;
}
}
DEFINE_JSON_TO_TAG(version)
{
if (value.is_string())
{
const auto& text = value.as_string();
if (text == "1.0") return version::v1;
if (text == "2.0") return version::v2;
}
return version::invalid;
}
// value_t
// ----------------------------------------------------------------------------
DEFINE_JSON_FROM_TAG(value_t)
{
std::visit(overload
{
[&] (null_t) NOEXCEPT
{
value = nullptr;
},
[&](boolean_t visit) NOEXCEPT
{
value = visit;
},
[&](number_t visit) NOEXCEPT
{
value = visit;
},
[&](const string_t& visit) NOEXCEPT
{
value = visit;
},
[&](const array_t& visit) NOEXCEPT
{
value = value_from(visit);
},
[&](const object_t& visit) NOEXCEPT
{
value = value_from(visit);
}
}, instance.inner);
}
DEFINE_JSON_TO_TAG(value_t)
{
if (value.is_null())
{
return { std::in_place_type<null_t> };
}
else if (value.is_bool())
{
return { std::in_place_type<boolean_t>, value.as_bool() };
}
else if (value.is_number())
{
return { std::in_place_type<number_t>, value.to_number<number_t>() };
}
else if (value.is_string())
{
return { std::in_place_type<string_t>, value.as_string() };
}
else if (value.is_array())
{
return { std::in_place_type<array_t>, value_to<array_t>(value) };
}
else if (value.is_object())
{
return { std::in_place_type<object_t>, value_to<object_t>(value) };
}
throw ostream_exception{ "value_t" };
}
// identity_t
// ----------------------------------------------------------------------------
DEFINE_JSON_FROM_TAG(identity_t)
{
std::visit(overload
{
[&] (null_t) NOEXCEPT
{
value = boost::json::value{};
},
[&](code_t visit) NOEXCEPT
{
value = visit;
},
[&](const string_t& visit) NOEXCEPT
{
value = visit;
}
}, instance);
}
DEFINE_JSON_TO_TAG(identity_t)
{
if (value.is_null())
{
return { null_t{} };
}
else if (value.is_number())
{
return { code_t{ value.to_number<code_t>() } };
}
else if (value.is_string())
{
return { string_t{ value.as_string() } };
}
throw ostream_exception{ "identity_t" };
}
// request_t
// ----------------------------------------------------------------------------
DEFINE_JSON_FROM_TAG(request_t)
{
json_object object{};
if (instance.jsonrpc != version::undefined)
{
object["jsonrpc"] = value_from(instance.jsonrpc);
}
if (instance.id.has_value())
{
object["id"] = value_from(instance.id.value());
}
if (!instance.method.empty())
{
object["method"] = value_from(instance.method);
}
if (instance.params.has_value())
{
if (const auto& params = instance.params.value();
std::holds_alternative<array_t>(params))
{
object["params"] = value_from(std::get<array_t>(params));
}
else
{
object["params"] = value_from(std::get<object_t>(params));
}
}
value = object;
}
// Bogus warnings on `it` not checked.
BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
DEFINE_JSON_TO_TAG(request_t)
{
request_t request{};
const auto& object = value.as_object();
// jsonrpc
if (const auto it = object.find("jsonrpc"); it != object.end())
{
request.jsonrpc = value_to<version>(it->value());
}
// method
if (const auto it = object.find("method"); it != object.end())
{
request.method = it->value().as_string();
}
// id
if (const auto it = object.find("id"); it != object.end())
{
request.id = value_to<identity_t>(it->value());
}
// params
if (const auto it = object.find("params"); it != object.end())
{
if (const auto& params = it->value(); params.is_array())
{
request.params = params_t
{
std::in_place_type<array_t>, value_to<array_t>(params)
};
}
else if (params.is_object())
{
request.params = params_t
{
std::in_place_type<object_t>, value_to<object_t>(params)
};
}
}
return request;
}
// response_t
// ----------------------------------------------------------------------------
DEFINE_JSON_FROM_TAG(response_t)
{
json_object object{};
if (instance.jsonrpc != version::undefined)
{
object["jsonrpc"] = value_from(instance.jsonrpc);
}
if (instance.id.has_value())
{
object["id"] = value_from(instance.id.value());
}
if (instance.error.has_value())
{
const auto& result = instance.error.value();
object["error"] =
{
{ "code", result.code },
{ "message", result.message }
};
if (result.data.has_value())
{
object["data"] = value_from(result.data.value());
}
}
if (instance.result.has_value())
{
object["result"] = value_from(instance.result.value());
}
value = object;
}
DEFINE_JSON_TO_TAG(response_t)
{
response_t response{};
const auto& object = value.as_object();
// jsonrpc
if (const auto it = object.find("jsonrpc"); it != object.end())
{
response.jsonrpc = value_to<version>(it->value());
}
// id
if (const auto it = object.find("id"); it != object.end())
{
if (const auto& id = it->value(); id.is_null())
{
response.id = { null_t{} };
}
else if (id.is_number())
{
response.id = { id.to_number<code_t>() };
}
else if (id.is_string())
{
response.id = { string_t{ id.as_string() } };
}
}
// error
if (const auto it = object.find("error"); it != object.end())
{
result_t result{};
const auto& error = it->value().as_object();
if (const auto code_it = error.find("code");
code_it != error.end())
{
result.code = code_it->value().to_number<code_t>();
}
if (const auto message_it = error.find("message");
message_it != error.end())
{
result.message = message_it->value().as_string();
}
if (const auto data_it = error.find("data");
data_it != error.end())
{
result.data = value_to<value_option>(data_it->value());
}
response.error = result;
}
// result
if (const auto it = object.find("result"); it != object.end())
{
response.result = value_to<value_option>(it->value());
}
return response;
}
BC_POP_WARNING()
} // namespace json
} // namespace network
} // namespace libbitcoin (edited)