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
 | 
			
		||||
// 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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										94
									
								
								src/json.cc
									
										
									
									
									
								
							
							
						
						
									
										94
									
								
								src/json.cc
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -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, ¤tIndent]()
 | 
			
		||||
    {
 | 
			
		||||
        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 += 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):
 | 
			
		||||
        {
 | 
			
		||||
            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 += "\"" + 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
 | 
			
		||||
| 
						 | 
				
			
			@ -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 //
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								src/json.h
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								src/json.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue