Commit bd9f49ef authored by Niels's avatar Niels

implemented pretty printing (issue #13)

- to_string() method is now called dump()
- syntax borrowed from Python’s json.dumps()
parent 08456b8f
......@@ -112,13 +112,13 @@ You can create an object (deserialization) by appending `_json` to a string lite
```cpp
// create object from string literal
json j = "{ \"pi\": 3.141, \"happy\": true }"_json;
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
// or even nicer (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp)
auto j2 = R"(
{
"pi": 3.141,
"happy": true
"happy": true,
"pi": 3.141
})"_json;
```
......@@ -126,7 +126,14 @@ You can also get a string representation (serialize):
```cpp
// explicit conversion to string
std::string s = j.to_string();
std::string s = j.dump(); // {\"happy\": true, \"pi\": 3.141}
// serialization with pretty printing
std::cout << j.dump(4) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
```
The value of s could be `{"pi": 3.141, "happy": true}`, but the order of the entries in the object is not fixed.
......
......@@ -472,8 +472,22 @@ json::operator object_t() const
return get<object_t>();
}
const std::string json::to_string() const noexcept
/*!
Internal implementation of the serialization function.
\param prettyPrint whether the output shall be pretty-printed
\param indentStep the indent level
\param currentIndent the current indent level (only used internally)
*/
const std::string json::dump(const bool prettyPrint,
const unsigned int indentStep, unsigned int currentIndent) const noexcept
{
// helper function to return whitespace as indentation
const auto indent = [prettyPrint, &currentIndent]()
{
return prettyPrint ? std::string(currentIndent, ' ') : std::string();
};
switch (type_)
{
case (value_type::string):
......@@ -498,34 +512,73 @@ const std::string json::to_string() const noexcept
case (value_type::array):
{
std::string result;
if (value_.array->empty())
{
return "[]";
}
std::string result = "[";
// increase indentation
if (prettyPrint)
{
currentIndent += indentStep;
result += "\n";
}
for (array_t::const_iterator i = value_.array->begin(); i != value_.array->end(); ++i)
{
if (i != value_.array->begin())
{
result += ", ";
result += prettyPrint ? ",\n" : ", ";
}
result += indent() + i->dump(prettyPrint, indentStep, currentIndent);
}
result += i->to_string();
// decrease indentation
if (prettyPrint)
{
currentIndent -= indentStep;
result += "\n";
}
return "[" + result + "]";
return result + indent() + "]";
}
case (value_type::object):
{
std::string result;
if (value_.object->empty())
{
return "{}";
}
std::string result = "{";
// increase indentation
if (prettyPrint)
{
currentIndent += indentStep;
result += "\n";
}
for (object_t::const_iterator i = value_.object->begin(); i != value_.object->end(); ++i)
{
if (i != value_.object->begin())
{
result += ", ";
result += prettyPrint ? ",\n" : ", ";
}
result += indent() + "\"" + i->first + "\": " + i->second.dump(prettyPrint, indentStep,
currentIndent);
}
result += "\"" + i->first + "\": " + i->second.to_string();
// decrease indentation
if (prettyPrint)
{
currentIndent -= indentStep;
result += "\n";
}
return "{" + result + "}";
return result + indent() + "}";
}
// actually only value_type::null - but making the compiler happy
......@@ -536,6 +589,29 @@ const std::string json::to_string() const noexcept
}
}
/*!
Serialization function for JSON objects. The function tries to mimick Python's
\p json.dumps() function, and currently supports its \p indent parameter.
\param indent if indent is nonnegative, then array elements and object members
will be pretty-printed with that indent level. An indent level
of 0 will only insert newlines. -1 (the default) selects the
most compact representation
\see https://docs.python.org/2/library/json.html#json.dump
*/
const std::string json::dump(int indent) const noexcept
{
if (indent >= 0)
{
return dump(true, static_cast<unsigned int>(indent));
}
else
{
return dump(false, 0);
}
}
///////////////////////////////////////////
// ADDING ELEMENTS TO OBJECTS AND ARRAYS //
......
......@@ -162,6 +162,9 @@ class json
/// return the type as string
const std::string type_name() const noexcept;
/// dump the object (with pretty printer)
const std::string dump(const bool, const unsigned int, unsigned int = 0) const noexcept;
public:
/// explicit value conversion
template<typename T>
......@@ -180,34 +183,34 @@ class json
/// implicit conversion to JSON map (only for objects)
operator object_t() const;
/// write to stream
/// serialize to stream
friend std::ostream& operator<<(std::ostream& o, const json& j)
{
o << j.to_string();
o << j.dump();
return o;
}
/// write to stream
/// serialize to stream
friend std::ostream& operator>>(const json& j, std::ostream& o)
{
o << j.to_string();
o << j.dump();
return o;
}
/// read from stream
/// deserialize from stream
friend std::istream& operator>>(std::istream& i, json& j)
{
j = parser(i).parse();
return i;
}
/// read from stream
/// deserialize from stream
friend std::istream& operator<<(json& j, std::istream& i)
{
j = parser(i).parse();
return i;
}
/// explicit conversion to string representation (C++ style)
const std::string to_string() const noexcept;
/// explicit serialization
const std::string dump(int = -1) const noexcept;
/// add an object/array to an array
json& operator+=(const json&);
......
......@@ -17,7 +17,7 @@ TEST_CASE("array")
const json j_const (j);
// string representation of default value
CHECK(j.to_string() == "[]");
CHECK(j.dump() == "[]");
// iterators
CHECK(j.begin() != j.end());
......@@ -305,7 +305,7 @@ TEST_CASE("object")
const json j_const = j;
// string representation of default value
CHECK(j.to_string() == "{}");
CHECK(j.dump() == "{}");
// iterators
CHECK(j.begin() != j.end());
......@@ -685,7 +685,7 @@ TEST_CASE("null")
CHECK(j.type() == json::value_type::null);
// string representation of default value
CHECK(j.to_string() == "null");
CHECK(j.dump() == "null");
// iterators
CHECK(j.begin() != j.end());
......@@ -755,7 +755,7 @@ TEST_CASE("string")
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.to_string() == "\"\"");
CHECK(j.dump() == "\"\"");
// container members
CHECK(j.size() == 1);
......@@ -837,7 +837,7 @@ TEST_CASE("boolean")
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.to_string() == "false");
CHECK(j.dump() == "false");
// container members
CHECK(j.size() == 1);
......@@ -916,7 +916,7 @@ TEST_CASE("number (int)")
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.to_string() == "0");
CHECK(j.dump() == "0");
// container members
CHECK(j.size() == 1);
......@@ -1002,7 +1002,7 @@ TEST_CASE("number (float)")
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.to_string() == "0.000000");
CHECK(j.dump() == "0.000000");
// container members
CHECK(j.size() == 1);
......@@ -1786,6 +1786,18 @@ TEST_CASE("Parser")
CHECK(j22 == j23);
}
SECTION("serialization")
{
auto j23 = "{ \"a\": null, \"b\": true, \"c\": [1,2,3], \"d\": {\"a\": 0} }"_json;
CHECK(j23.dump() == "{\"a\": null, \"b\": true, \"c\": [1, 2, 3], \"d\": {\"a\": 0}}");
CHECK(j23.dump(-1) == "{\"a\": null, \"b\": true, \"c\": [1, 2, 3], \"d\": {\"a\": 0}}");
CHECK(j23.dump(0) ==
"{\n\"a\": null,\n\"b\": true,\n\"c\": [\n1,\n2,\n3\n],\n\"d\": {\n\"a\": 0\n}\n}");
CHECK(j23.dump(4) ==
"{\n \"a\": null,\n \"b\": true,\n \"c\": [\n 1,\n 2,\n 3\n ],\n \"d\": {\n \"a\": 0\n }\n}");
}
SECTION("Errors")
{
CHECK_THROWS_AS(json::parse(""), std::invalid_argument);
......
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