Commit 7546bfce authored by Dominik Charousset's avatar Dominik Charousset

Add function for printing JSON objects

parent 50984474
...@@ -161,6 +161,7 @@ caf_add_component( ...@@ -161,6 +161,7 @@ caf_add_component(
src/ipv6_address.cpp src/ipv6_address.cpp
src/ipv6_endpoint.cpp src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp src/ipv6_subnet.cpp
src/json_array.cpp
src/json_object.cpp src/json_object.cpp
src/json_reader.cpp src/json_reader.cpp
src/json_value.cpp src/json_value.cpp
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#pragma once #pragma once
#include "caf/detail/monotonic_buffer_resource.hpp" #include "caf/detail/monotonic_buffer_resource.hpp"
#include "caf/detail/print.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/parser_state.hpp" #include "caf/parser_state.hpp"
#include "caf/ref_counted.hpp" #include "caf/ref_counted.hpp"
...@@ -657,4 +658,123 @@ value* parse_shallow(string_parser_state& ps, ...@@ -657,4 +658,123 @@ value* parse_shallow(string_parser_state& ps,
value* parse_in_situ(mutable_string_parser_state& ps, value* parse_in_situ(mutable_string_parser_state& ps,
monotonic_buffer_resource* storage); monotonic_buffer_resource* storage);
// -- printing -----------------------------------------------------------------
template <class Buffer>
static void print_to(Buffer& buf, std::string_view str) {
buf.insert(buf.end(), str.begin(), str.end());
}
template <class Buffer>
static void print_nl_to(Buffer& buf, size_t indentation) {
buf.push_back('\n');
buf.insert(buf.end(), indentation, ' ');
}
template <class Buffer>
void print_to(Buffer& buf, const array& arr, size_t indentation_factor,
size_t offset = 0);
template <class Buffer>
void print_to(Buffer& buf, const object& obj, size_t indentation_factor,
size_t offset = 0);
template <class Buffer>
void print_to(Buffer& buf, const value& val, size_t indentation_factor,
size_t offset = 0) {
using namespace std::literals;
switch (val.data.index()) {
case value::integer_index:
print(buf, std::get<int64_t>(val.data));
break;
case value::double_index:
print(buf, std::get<double>(val.data));
break;
case value::bool_index:
print(buf, std::get<bool>(val.data));
break;
case value::string_index:
print_escaped(buf, std::get<std::string_view>(val.data));
break;
case value::array_index:
print_to(buf, std::get<array>(val.data), indentation_factor, offset);
break;
case value::object_index:
print_to(buf, std::get<object>(val.data), indentation_factor, offset);
break;
default:
print_to(buf, "null"sv);
}
}
template <class Buffer>
void print_to(Buffer& buf, const array& arr, size_t indentation_factor,
size_t offset) {
using namespace std::literals;
if (arr.empty()) {
print_to(buf, "[]"sv);
} else if (indentation_factor == 0) {
buf.push_back('[');
auto i = arr.begin();
print_to(buf, *i, 0);
auto e = arr.end();
while (++i != e) {
print_to(buf, ", "sv);
print_to(buf, *i, 0);
}
buf.push_back(']');
} else {
buf.push_back('[');
auto new_offset = indentation_factor + offset;
print_nl_to(buf, new_offset);
auto i = arr.begin();
print_to(buf, *i, indentation_factor, new_offset);
auto e = arr.end();
while (++i != e) {
buf.push_back(',');
print_nl_to(buf, new_offset);
print_to(buf, *i, indentation_factor, new_offset);
}
print_nl_to(buf, offset);
buf.push_back(']');
}
}
template <class Buffer>
void print_to(Buffer& buf, const object& obj, size_t indentation_factor,
size_t offset) {
using namespace std::literals;
auto print_member = [&](const value::member& kvp, size_t new_offset) {
print_escaped(buf, kvp.key);
print_to(buf, ": "sv);
print_to(buf, *kvp.val, indentation_factor, new_offset);
};
if (obj.empty()) {
print_to(buf, "{}"sv);
} else if (indentation_factor == 0) {
buf.push_back('{');
auto i = obj.begin();
print_member(*i, offset);
auto e = obj.end();
while (++i != e) {
print_to(buf, ", "sv);
print_member(*i, offset);
}
buf.push_back('}');
} else {
buf.push_back('{');
auto new_offset = indentation_factor + offset;
print_nl_to(buf, new_offset);
auto i = obj.begin();
print_member(*i, new_offset);
auto e = obj.end();
while (++i != e) {
buf.push_back(',');
print_nl_to(buf, new_offset);
print_member(*i, new_offset);
}
print_nl_to(buf, offset);
buf.push_back('}');
}
}
} // namespace caf::detail::json } // namespace caf::detail::json
...@@ -114,6 +114,13 @@ public: ...@@ -114,6 +114,13 @@ public:
return {arr_->end(), storage_.get()}; return {arr_->end(), storage_.get()};
} }
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *arr_, indentation_factor);
}
// -- serialization ---------------------------------------------------------- // -- serialization ----------------------------------------------------------
template <class Inspector> template <class Inspector>
...@@ -141,6 +148,8 @@ private: ...@@ -141,6 +148,8 @@ private:
detail::json::storage_ptr storage_; detail::json::storage_ptr storage_;
}; };
// -- free functions -----------------------------------------------------------
inline bool operator==(const json_array::const_iterator& lhs, inline bool operator==(const json_array::const_iterator& lhs,
const json_array::const_iterator& rhs) noexcept { const json_array::const_iterator& rhs) noexcept {
return lhs.equal_to(rhs); return lhs.equal_to(rhs);
...@@ -159,4 +168,7 @@ inline bool operator!=(const json_array& lhs, const json_array& rhs) noexcept { ...@@ -159,4 +168,7 @@ inline bool operator!=(const json_array& lhs, const json_array& rhs) noexcept {
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates json_array
CAF_CORE_EXPORT std::string to_string(const json_array& arr);
} // namespace caf } // namespace caf
...@@ -119,6 +119,13 @@ public: ...@@ -119,6 +119,13 @@ public:
return {obj_->end(), storage_.get()}; return {obj_->end(), storage_.get()};
} }
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *obj_, indentation_factor);
}
// -- serialization ---------------------------------------------------------- // -- serialization ----------------------------------------------------------
template <class Inspector> template <class Inspector>
...@@ -165,4 +172,7 @@ inline bool operator!=(const json_object& lhs, ...@@ -165,4 +172,7 @@ inline bool operator!=(const json_object& lhs,
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates json_object
CAF_CORE_EXPORT std::string to_string(const json_object& val);
} // namespace caf } // namespace caf
...@@ -134,6 +134,13 @@ public: ...@@ -134,6 +134,13 @@ public:
/// objects created from that value. /// objects created from that value.
static expected<json_value> parse_in_situ(std::string& str); static expected<json_value> parse_in_situ(std::string& str);
// -- printing ---------------------------------------------------------------
template <class Buffer>
void print_to(Buffer& buf, size_t indentation_factor = 0) const {
detail::json::print_to(buf, *val_, indentation_factor);
}
// -- serialization ---------------------------------------------------------- // -- serialization ----------------------------------------------------------
template <class Inspector> template <class Inspector>
...@@ -155,6 +162,8 @@ private: ...@@ -155,6 +162,8 @@ private:
detail::json::storage_ptr storage_; detail::json::storage_ptr storage_;
}; };
// -- free functions -----------------------------------------------------------
inline bool operator==(const json_value& lhs, const json_value& rhs) { inline bool operator==(const json_value& lhs, const json_value& rhs) {
return lhs.equal_to(rhs); return lhs.equal_to(rhs);
} }
...@@ -163,4 +172,7 @@ inline bool operator!=(const json_value& lhs, const json_value& rhs) { ...@@ -163,4 +172,7 @@ inline bool operator!=(const json_value& lhs, const json_value& rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
/// @relates json_value
CAF_CORE_EXPORT std::string to_string(const json_value& val);
} // namespace caf } // namespace caf
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_array.hpp"
namespace caf {
std::string to_string(const json_array& arr) {
std::string result;
arr.print_to(result);
return result;
}
} // namespace caf
...@@ -17,4 +17,10 @@ json_value json_object::value(std::string_view key) const { ...@@ -17,4 +17,10 @@ json_value json_object::value(std::string_view key) const {
return json_value::undefined(); return json_value::undefined();
} }
std::string to_string(const json_object& obj) {
std::string result;
obj.print_to(result);
return result;
}
} // namespace caf } // namespace caf
...@@ -121,4 +121,10 @@ expected<json_value> json_value::parse_in_situ(std::string& str) { ...@@ -121,4 +121,10 @@ expected<json_value> json_value::parse_in_situ(std::string& str) {
} }
} }
std::string to_string(const json_value& val) {
std::string result;
val.print_to(result);
return result;
}
} // namespace caf } // namespace caf
...@@ -14,12 +14,24 @@ ...@@ -14,12 +14,24 @@
using namespace caf; using namespace caf;
using namespace std::literals; using namespace std::literals;
namespace {
std::string printed(const json_array& arr) {
std::string result;
arr.print_to(result, 2);
return result;
}
} // namespace
TEST_CASE("default-constructed") { TEST_CASE("default-constructed") {
auto arr = json_array{}; auto arr = json_array{};
CHECK(arr.empty()); CHECK(arr.empty());
CHECK(arr.is_empty()); CHECK(arr.is_empty());
CHECK(arr.begin() == arr.end()); CHECK(arr.begin() == arr.end());
CHECK_EQ(arr.size(), 0u); CHECK_EQ(arr.size(), 0u);
CHECK_EQ(to_string(arr), "[]");
CHECK_EQ(printed(arr), "[]");
CHECK_EQ(deep_copy(arr), arr); CHECK_EQ(deep_copy(arr), arr);
} }
...@@ -29,6 +41,8 @@ TEST_CASE("from empty array") { ...@@ -29,6 +41,8 @@ TEST_CASE("from empty array") {
CHECK(arr.is_empty()); CHECK(arr.is_empty());
CHECK(arr.begin() == arr.end()); CHECK(arr.begin() == arr.end());
CHECK_EQ(arr.size(), 0u); CHECK_EQ(arr.size(), 0u);
CHECK_EQ(to_string(arr), "[]");
CHECK_EQ(printed(arr), "[]");
CHECK_EQ(deep_copy(arr), arr); CHECK_EQ(deep_copy(arr), arr);
} }
...@@ -47,5 +61,7 @@ TEST_CASE("from non-empty array") { ...@@ -47,5 +61,7 @@ TEST_CASE("from non-empty array") {
CHECK_EQ(vals[0].to_integer(), 1); CHECK_EQ(vals[0].to_integer(), 1);
CHECK_EQ(vals[1].to_string(), "two"); CHECK_EQ(vals[1].to_string(), "two");
CHECK_EQ(vals[2].to_double(), 3.0); CHECK_EQ(vals[2].to_double(), 3.0);
CHECK_EQ(to_string(arr), R"_([1, "two", 3])_");
CHECK_EQ(printed(arr), "[\n 1,\n \"two\",\n 3\n]");
CHECK_EQ(deep_copy(arr), arr); CHECK_EQ(deep_copy(arr), arr);
} }
...@@ -14,6 +14,16 @@ ...@@ -14,6 +14,16 @@
using namespace caf; using namespace caf;
using namespace std::literals; using namespace std::literals;
namespace {
std::string printed(const json_object& obj) {
std::string result;
obj.print_to(result, 2);
return result;
}
} // namespace
TEST_CASE("default-constructed") { TEST_CASE("default-constructed") {
auto obj = json_object{}; auto obj = json_object{};
CHECK(obj.empty()); CHECK(obj.empty());
...@@ -21,6 +31,8 @@ TEST_CASE("default-constructed") { ...@@ -21,6 +31,8 @@ TEST_CASE("default-constructed") {
CHECK(obj.begin() == obj.end()); CHECK(obj.begin() == obj.end());
CHECK_EQ(obj.size(), 0u); CHECK_EQ(obj.size(), 0u);
CHECK(obj.value("foo").is_undefined()); CHECK(obj.value("foo").is_undefined());
CHECK_EQ(to_string(obj), "{}");
CHECK_EQ(printed(obj), "{}");
CHECK_EQ(deep_copy(obj), obj); CHECK_EQ(deep_copy(obj), obj);
} }
...@@ -31,6 +43,8 @@ TEST_CASE("from empty object") { ...@@ -31,6 +43,8 @@ TEST_CASE("from empty object") {
CHECK(obj.begin() == obj.end()); CHECK(obj.begin() == obj.end());
CHECK_EQ(obj.size(), 0u); CHECK_EQ(obj.size(), 0u);
CHECK(obj.value("foo").is_undefined()); CHECK(obj.value("foo").is_undefined());
CHECK_EQ(to_string(obj), "{}");
CHECK_EQ(printed(obj), "{}");
CHECK_EQ(deep_copy(obj), obj); CHECK_EQ(deep_copy(obj), obj);
} }
...@@ -54,5 +68,7 @@ TEST_CASE("from non-empty object") { ...@@ -54,5 +68,7 @@ TEST_CASE("from non-empty object") {
CHECK_EQ(vals[0].second.to_string(), "one"); CHECK_EQ(vals[0].second.to_string(), "one");
CHECK_EQ(vals[1].first, "b"); CHECK_EQ(vals[1].first, "b");
CHECK_EQ(vals[1].second.to_integer(), 2); CHECK_EQ(vals[1].second.to_integer(), 2);
CHECK_EQ(to_string(obj), R"_({"a": "one", "b": 2})_");
CHECK_EQ(printed(obj), "{\n \"a\": \"one\",\n \"b\": 2\n}");
CHECK_EQ(deep_copy(obj), obj); CHECK_EQ(deep_copy(obj), obj);
} }
...@@ -16,6 +16,16 @@ ...@@ -16,6 +16,16 @@
using namespace caf; using namespace caf;
using namespace std::literals; using namespace std::literals;
namespace {
std::string printed(const json_value& val) {
std::string result;
val.print_to(result, 2);
return result;
}
} // namespace
TEST_CASE("default-constructed") { TEST_CASE("default-constructed") {
auto val = json_value{}; auto val = json_value{};
CHECK(val.is_null()); CHECK(val.is_null());
...@@ -33,6 +43,8 @@ TEST_CASE("default-constructed") { ...@@ -33,6 +43,8 @@ TEST_CASE("default-constructed") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "null");
CHECK_EQ(printed(val), "null");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -53,6 +65,8 @@ TEST_CASE("from undefined") { ...@@ -53,6 +65,8 @@ TEST_CASE("from undefined") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "null");
CHECK_EQ(printed(val), "null");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -72,6 +86,8 @@ TEST_CASE("from integer") { ...@@ -72,6 +86,8 @@ TEST_CASE("from integer") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "42");
CHECK_EQ(printed(val), "42");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -91,6 +107,8 @@ TEST_CASE("from double") { ...@@ -91,6 +107,8 @@ TEST_CASE("from double") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "42");
CHECK_EQ(printed(val), "42");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -110,7 +128,8 @@ TEST_CASE("from bool") { ...@@ -110,7 +128,8 @@ TEST_CASE("from bool") {
CHECK_EQ(val.to_bool(), true); CHECK_EQ(val.to_bool(), true);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
MESSAGE(deep_to_string(val)); CHECK_EQ(to_string(val), "true");
CHECK_EQ(printed(val), "true");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -130,6 +149,8 @@ TEST_CASE("from string") { ...@@ -130,6 +149,8 @@ TEST_CASE("from string") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), "Hello, world!"sv); CHECK_EQ(val.to_string(), "Hello, world!"sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), R"_("Hello, world!")_");
CHECK_EQ(printed(val), R"_("Hello, world!")_");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -150,10 +171,34 @@ TEST_CASE("from empty array") { ...@@ -150,10 +171,34 @@ TEST_CASE("from empty array") {
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_array().size(), 0u); CHECK_EQ(val.to_array().size(), 0u);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "[]");
CHECK_EQ(printed(val), "[]");
CHECK_EQ(deep_copy(val), val);
}
TEST_CASE("from array of size 1") {
auto val = unbox(json_value::parse("[1]"));
CHECK(!val.is_null());
CHECK(!val.is_undefined());
CHECK(!val.is_integer());
CHECK(!val.is_double());
CHECK(!val.is_number());
CHECK(!val.is_bool());
CHECK(!val.is_string());
CHECK(val.is_array());
CHECK(!val.is_object());
CHECK_EQ(val.to_integer(), 0);
CHECK_EQ(val.to_double(), 0.0);
CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_array().size(), 1u);
CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "[1]");
CHECK_EQ(printed(val), "[\n 1\n]");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
TEST_CASE("from non-empty array") { TEST_CASE("from array of size 3") {
auto val = unbox(json_value::parse("[1, 2, 3]")); auto val = unbox(json_value::parse("[1, 2, 3]"));
CHECK(!val.is_null()); CHECK(!val.is_null());
CHECK(!val.is_undefined()); CHECK(!val.is_undefined());
...@@ -170,6 +215,8 @@ TEST_CASE("from non-empty array") { ...@@ -170,6 +215,8 @@ TEST_CASE("from non-empty array") {
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_array().size(), 3u); CHECK_EQ(val.to_array().size(), 3u);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "[1, 2, 3]");
CHECK_EQ(printed(val), "[\n 1,\n 2,\n 3\n]");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -189,6 +236,8 @@ TEST_CASE("from empty object") { ...@@ -189,6 +236,8 @@ TEST_CASE("from empty object") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 0u); CHECK_EQ(val.to_object().size(), 0u);
CHECK_EQ(to_string(val), "{}");
CHECK_EQ(printed(val), "{}");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
...@@ -208,5 +257,7 @@ TEST_CASE("from non-empty object") { ...@@ -208,5 +257,7 @@ TEST_CASE("from non-empty object") {
CHECK_EQ(val.to_bool(), false); CHECK_EQ(val.to_bool(), false);
CHECK_EQ(val.to_string(), ""sv); CHECK_EQ(val.to_string(), ""sv);
CHECK_EQ(val.to_object().size(), 1u); CHECK_EQ(val.to_object().size(), 1u);
CHECK_EQ(to_string(val), R"_({"foo": "bar"})_");
CHECK_EQ(printed(val), "{\n \"foo\": \"bar\"\n}");
CHECK_EQ(deep_copy(val), val); CHECK_EQ(deep_copy(val), val);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment