implemented pretty printing (issue #13)
- to_string() method is now called dump() - syntax borrowed from Python’s json.dumps()
This commit is contained in:
parent
08456b8ff0
commit
bd9f49efb9
4 changed files with 126 additions and 28 deletions
15
README.md
15
README.md
|
@ -112,13 +112,13 @@ You can create an object (deserialization) by appending `_json` to a string lite
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// create object from string literal
|
// 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)
|
// or even nicer (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp)
|
||||||
auto j2 = R"(
|
auto j2 = R"(
|
||||||
{
|
{
|
||||||
"pi": 3.141,
|
"happy": true,
|
||||||
"happy": true
|
"pi": 3.141
|
||||||
})"_json;
|
})"_json;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -126,7 +126,14 @@ You can also get a string representation (serialize):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// explicit conversion to string
|
// 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.
|
The value of s could be `{"pi": 3.141, "happy": true}`, but the order of the entries in the object is not fixed.
|
||||||
|
|
94
src/json.cc
94
src/json.cc
|
@ -472,8 +472,22 @@ json::operator object_t() const
|
||||||
return get<object_t>();
|
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, ¤tIndent]()
|
||||||
|
{
|
||||||
|
return prettyPrint ? std::string(currentIndent, ' ') : std::string();
|
||||||
|
};
|
||||||
|
|
||||||
switch (type_)
|
switch (type_)
|
||||||
{
|
{
|
||||||
case (value_type::string):
|
case (value_type::string):
|
||||||
|
@ -498,34 +512,73 @@ const std::string json::to_string() const noexcept
|
||||||
|
|
||||||
case (value_type::array):
|
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)
|
for (array_t::const_iterator i = value_.array->begin(); i != value_.array->end(); ++i)
|
||||||
{
|
{
|
||||||
if (i != value_.array->begin())
|
if (i != value_.array->begin())
|
||||||
{
|
{
|
||||||
result += ", ";
|
result += prettyPrint ? ",\n" : ", ";
|
||||||
}
|
}
|
||||||
result += i->to_string();
|
result += indent() + i->dump(prettyPrint, indentStep, currentIndent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "[" + result + "]";
|
// decrease indentation
|
||||||
|
if (prettyPrint)
|
||||||
|
{
|
||||||
|
currentIndent -= indentStep;
|
||||||
|
result += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + indent() + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
case (value_type::object):
|
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)
|
for (object_t::const_iterator i = value_.object->begin(); i != value_.object->end(); ++i)
|
||||||
{
|
{
|
||||||
if (i != value_.object->begin())
|
if (i != value_.object->begin())
|
||||||
{
|
{
|
||||||
result += ", ";
|
result += prettyPrint ? ",\n" : ", ";
|
||||||
}
|
}
|
||||||
result += "\"" + i->first + "\": " + i->second.to_string();
|
result += indent() + "\"" + i->first + "\": " + i->second.dump(prettyPrint, indentStep,
|
||||||
|
currentIndent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "{" + result + "}";
|
// decrease indentation
|
||||||
|
if (prettyPrint)
|
||||||
|
{
|
||||||
|
currentIndent -= indentStep;
|
||||||
|
result += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result + indent() + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually only value_type::null - but making the compiler happy
|
// 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 //
|
// ADDING ELEMENTS TO OBJECTS AND ARRAYS //
|
||||||
|
|
19
src/json.h
19
src/json.h
|
@ -162,6 +162,9 @@ class json
|
||||||
/// return the type as string
|
/// return the type as string
|
||||||
const std::string type_name() const noexcept;
|
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:
|
public:
|
||||||
/// explicit value conversion
|
/// explicit value conversion
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -180,34 +183,34 @@ class json
|
||||||
/// implicit conversion to JSON map (only for objects)
|
/// implicit conversion to JSON map (only for objects)
|
||||||
operator object_t() const;
|
operator object_t() const;
|
||||||
|
|
||||||
/// write to stream
|
/// serialize to stream
|
||||||
friend std::ostream& operator<<(std::ostream& o, const json& j)
|
friend std::ostream& operator<<(std::ostream& o, const json& j)
|
||||||
{
|
{
|
||||||
o << j.to_string();
|
o << j.dump();
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
/// write to stream
|
/// serialize to stream
|
||||||
friend std::ostream& operator>>(const json& j, std::ostream& o)
|
friend std::ostream& operator>>(const json& j, std::ostream& o)
|
||||||
{
|
{
|
||||||
o << j.to_string();
|
o << j.dump();
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read from stream
|
/// deserialize from stream
|
||||||
friend std::istream& operator>>(std::istream& i, json& j)
|
friend std::istream& operator>>(std::istream& i, json& j)
|
||||||
{
|
{
|
||||||
j = parser(i).parse();
|
j = parser(i).parse();
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
/// read from stream
|
/// deserialize from stream
|
||||||
friend std::istream& operator<<(json& j, std::istream& i)
|
friend std::istream& operator<<(json& j, std::istream& i)
|
||||||
{
|
{
|
||||||
j = parser(i).parse();
|
j = parser(i).parse();
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// explicit conversion to string representation (C++ style)
|
/// explicit serialization
|
||||||
const std::string to_string() const noexcept;
|
const std::string dump(int = -1) const noexcept;
|
||||||
|
|
||||||
/// add an object/array to an array
|
/// add an object/array to an array
|
||||||
json& operator+=(const json&);
|
json& operator+=(const json&);
|
||||||
|
|
|
@ -17,7 +17,7 @@ TEST_CASE("array")
|
||||||
const json j_const (j);
|
const json j_const (j);
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "[]");
|
CHECK(j.dump() == "[]");
|
||||||
|
|
||||||
// iterators
|
// iterators
|
||||||
CHECK(j.begin() != j.end());
|
CHECK(j.begin() != j.end());
|
||||||
|
@ -305,7 +305,7 @@ TEST_CASE("object")
|
||||||
const json j_const = j;
|
const json j_const = j;
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "{}");
|
CHECK(j.dump() == "{}");
|
||||||
|
|
||||||
// iterators
|
// iterators
|
||||||
CHECK(j.begin() != j.end());
|
CHECK(j.begin() != j.end());
|
||||||
|
@ -685,7 +685,7 @@ TEST_CASE("null")
|
||||||
CHECK(j.type() == json::value_type::null);
|
CHECK(j.type() == json::value_type::null);
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "null");
|
CHECK(j.dump() == "null");
|
||||||
|
|
||||||
// iterators
|
// iterators
|
||||||
CHECK(j.begin() != j.end());
|
CHECK(j.begin() != j.end());
|
||||||
|
@ -755,7 +755,7 @@ TEST_CASE("string")
|
||||||
CHECK(j.cbegin() != j.cend());
|
CHECK(j.cbegin() != j.cend());
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "\"\"");
|
CHECK(j.dump() == "\"\"");
|
||||||
|
|
||||||
// container members
|
// container members
|
||||||
CHECK(j.size() == 1);
|
CHECK(j.size() == 1);
|
||||||
|
@ -837,7 +837,7 @@ TEST_CASE("boolean")
|
||||||
CHECK(j.cbegin() != j.cend());
|
CHECK(j.cbegin() != j.cend());
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "false");
|
CHECK(j.dump() == "false");
|
||||||
|
|
||||||
// container members
|
// container members
|
||||||
CHECK(j.size() == 1);
|
CHECK(j.size() == 1);
|
||||||
|
@ -916,7 +916,7 @@ TEST_CASE("number (int)")
|
||||||
CHECK(j.cbegin() != j.cend());
|
CHECK(j.cbegin() != j.cend());
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "0");
|
CHECK(j.dump() == "0");
|
||||||
|
|
||||||
// container members
|
// container members
|
||||||
CHECK(j.size() == 1);
|
CHECK(j.size() == 1);
|
||||||
|
@ -1002,7 +1002,7 @@ TEST_CASE("number (float)")
|
||||||
CHECK(j.cbegin() != j.cend());
|
CHECK(j.cbegin() != j.cend());
|
||||||
|
|
||||||
// string representation of default value
|
// string representation of default value
|
||||||
CHECK(j.to_string() == "0.000000");
|
CHECK(j.dump() == "0.000000");
|
||||||
|
|
||||||
// container members
|
// container members
|
||||||
CHECK(j.size() == 1);
|
CHECK(j.size() == 1);
|
||||||
|
@ -1786,6 +1786,18 @@ TEST_CASE("Parser")
|
||||||
CHECK(j22 == j23);
|
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")
|
SECTION("Errors")
|
||||||
{
|
{
|
||||||
CHECK_THROWS_AS(json::parse(""), std::invalid_argument);
|
CHECK_THROWS_AS(json::parse(""), std::invalid_argument);
|
||||||
|
|
Loading…
Reference in a new issue