JSON (ECMA-404 and JSON5) parser & stringifier written in C++11.
- Easy to embed to your project. Only single file
json5pp.hpp
required. - Parse standard JSON (ECMA-404) from
std::istream
orstd::string
. - Parse JSON5 from
std::istream
orstd::string
. - Stringify to
std::ostream
orstd::string
as standard JSON. - Stringify to
std::ostream
orstd::string
as JSON5.
- Compilers with C++11 support
- MIT license
namespace json5pp {
class value {
};
}
- The class holds a value which is represented in JSON.
- This class can hold all types in JSON:
null
- boolean (
true
/false
) - number
- string
- array (stored as
std::vector<json5pp::value>
) - object (stored as
std::map<std::string, json5pp::value>
)
- Provides type check with
is_xxx()
method. (xxx
is one ofnull
,boolean
,number
,string
,array
andobject
) - Provides explicit cast to C++ type with
as_xxx()
method. (xxx
is one ofnull
,boolean
,number
,integer
,string
,array
andobject
)- If cast failed, throws
std::bad_cast
- If cast failed, throws
- Accepts implicit cast (by overload of
operator=
) from C++ type (nullptr_t
,bool
,double
|int
,std::string
|const char*
) - See examples for details
namespace json5pp {
value parse(const std::string& str);
}
- Parse given string.
- String must be a valid JSON (ECMA-404 standard)
- If not valid, throws
json5pp::syntax_error
.
- If not valid, throws
- String must be a finished (closed) JSON.
- i.e. If there are any junk data (except for spaces) after JSON, throws
json5pp::syntax_error
.
- i.e. If there are any junk data (except for spaces) after JSON, throws
namespace json5pp {
value parse(std::istream& istream, bool finish = true)
}
- Parse JSON from given input stream.
- Stream must have a valid JSON (ECMA-404 standard).
- If not valid, throws
json5pp::syntax_error
.
- If not valid, throws
- If
finish
is true, stream must be closed by eof after JSON.
namespace json5pp {
value parse5(const std::string& str);
}
- JSON5 version of
parse(const std::string&)
namespace json5pp {
value parse5(std::istream& istream, bool finish = true);
}
- JSON5 version of
parse(std::istream&, bool)
namespace json5pp {
class value {
template <class... T>
std::string stringify(T... manip);
};
}
- Stringify value to ECMA-404 standard JSON.
- About
T... manip
, see iostream overloads and manipulators
namespace json5pp {
class value {
template <class... T>
std::string stringify5(T... manip);
};
}
- Stringify value to JSON5.
- About
T... manip
, see iostream overloads and manipulators
namespace json5pp {
template <class... T>
std::string stringify(const value& v, T... manip);
}
- Global method version of
json5pp::value::stringify()
- Same as
v.stringify(manip...)
namespace json5pp {
template <class... T>
std::string stringify5(const value& v, T... manip);
}
- Global method version of
json5pp::value::stringify5()
- Same as
v.stringify5(manip...)
std::istream& istream = ...;
json5pp::value v;
istream >> v;
std::istream& istream = ...;
json5pp::value v;
istream >> json5pp::rule::json5() >> v; // Parse as JSON5
std::ostream& ostream = ...;
const json5pp::value& v = ...;
ostream << v;
std::ostream& ostream = ...;
const json5pp::value& v = ...;
ostream << json5pp::rule::tab_indent<1>() << v; // Stringify with tab indent
ostream << json5pp::rule::space_indent<2>() << v; // Stringify with 2-space indent
json5pp::rule::single_line_comment()
json5pp::rule::no_single_line_comment()
- Allow/disallow single line comment starts with
//
- Allow/disallow single line comment starts with
json5pp::rule::multi_line_comment()
json5pp::rule::no_multi_line_comment()
- Allow/disallow multiple line comment starts with
/*
and ends with*/
- Allow/disallow multiple line comment starts with
json5pp::rule::comments()
json5pp::rule::no_comments()
- Combination of
single_line_comment
andmulti_line_comment
- Combination of
json5pp::rule::explicit_plus_sign()
json5pp::rule::no_explicit_plus_sign()
- Allow/disallow explicit plus sign (
+
) before non-negative number (ex:+123
)
- Allow/disallow explicit plus sign (
json5pp::rule::leading_decimal_point()
json5pp::rule::no_leading_decimal_point()
- Allow/disallow leading decimal point before number (ex:
.123
)
- Allow/disallow leading decimal point before number (ex:
json5pp::rule::trailing_decimal_point()
json5pp::rule::no_trailing_decimal_point()
- Allow/disallow trailing decimal point after number (ex:
123.
)
- Allow/disallow trailing decimal point after number (ex:
json5pp::rule::decimal_points()
json5pp::rule::no_decimal_points()
- Combination of
leading_decimal_point
andtrailing_decimal_point
- Combination of
json5pp::rule::infinity_number()
json5pp::rule::no_infinity_number()
- Allow/disallow infinity number
json5pp::rule::not_a_number()
json5pp::rule::no_not_a_number()
- Allow/disallow NaN
json5pp::rule::hexadecimal()
json5pp::rule::no_hexadecimal()
- Allow/disallow hexadecimal number (ex:
0x123
)
- Allow/disallow hexadecimal number (ex:
json5pp::rule::single_quote()
json5pp::rule::no_single_quote()
- Allow/disallow single quoted string (ex:
'foobar'
)
- Allow/disallow single quoted string (ex:
json5pp::rule::multi_line_string()
json5pp::rule::no_multi_line_string()
- Allow/disallow multiple line string escaped by
\
- Example:
"test\ 2nd line"
- Allow/disallow multiple line string escaped by
json5pp::rule::trailing_comma()
json5pp::rule::no_trailing_comma()
- Allow/disallow trailing comma at the end of arrays or objects.
- Example for arrays:
[1,2,3,]
- Example for objects:
{"a":123,}
json5pp::rule::unquoted_key()
json5pp::rule::no_unquoted_key()
- Allow/disallow unquoted keys in objects. (ex:
{a:123}
)
- Allow/disallow unquoted keys in objects. (ex:
json5pp::rule::ecma404()
- ECMA-404 standard rule set.
json5pp::rule::json5()
- JSON5 rule set.
json5pp::rule::finished()
- Parse as finished (closed) JSON. If any junk data follows after JSON, parse fails.
- Opposite to
json5pp::rule::streaming()
json5pp::rule::streaming()
- Parse as non-finished (non-closed) JSON. Parse will succeed at the end of JSON.
- Opposite to
json5pp::rule::finished()
json5pp::rule::lf_newline()
- When indent is enabled, use LF(
\n
) as new-line code. - Opposite to
json5pp::rule::crlf_newline
- When indent is enabled, use LF(
json5pp::rule::crlf_newline()
- When indent is enabled, use CR+LF(
\r\n
) as new-line code. - Opposite to
json5pp::rule::lf_newline
- When indent is enabled, use CR+LF(
json5pp::rule::no_indent()
- Disable indent. All arrays and objects will be stringified as one-line.
json5pp::rule::tab_indent<L>()
- Enable indent with tab character(s).
L
means a number of tab (\t
) characters for one-level indent.- If
L
is omitted, treat asL=1
.
json5pp::rule::space_indent<L>()
- Enable indent with space character(s).
L
means a number of space (- If
L
is omitted, treat asL=2
.
using namespace std;
// Construct "null" value
json5pp::value a; // Default constructor
cout << a.is_null() << endl; // => 1
cout << a << endl; // => null
json5pp::value b(nullptr); // Constructor with std::nullptr_t argument
cout << b.is_null() << endl; // => 1
cout << b << endl; // => null
// json5pp::value c(NULL); // Compile error
// NULL cannot be used instead of nullptr
// Construct boolean value
json5pp::value d(true); // Constructor with bool argument
cout << d.is_boolean() << endl; // => 1
cout << d << endl; // => true
// Construct number value
json5pp::value e(123.45); // Constructor with double argument
cout << e.is_number() << endl; // => 1
cout << e << endl; // => 123.45
json5pp::value f(789); // Constructor with int argument
cout << f.is_number() << endl; // => 1
cout << f << endl; // => 789
// Construct string value
std::string str("foo");
json5pp::value g(str); // Constructor with const std::string& argument
cout << g.is_string() << endl; // => 1
cout << g << endl; // => "foo"
json5pp::value h("bar"); // Constructor with const char* argument
cout << h.is_string() << endl; // => 1
cout << h << endl; // => "bar"
// Construct array value
json5pp::value i {1, false, "baz", nullptr};
// Construct with std::initializer_list
cout << i.is_array() << endl; // => 1
cout << i << endl; // => [1,false,"baz",null]
auto j = json5pp::array({1, false, "baz", nullptr});
// Construct with utility function: json5pp::array()
cout << j.is_array() << endl; // => 1
cout << j << endl; // => [1,false,"baz",null]
// json5pp::value k {{"foo", 123}};
// Compile error (*1)
// Construct object value
auto m = json5pp::object({{"bar", 123}, {"foo", "baz"}});
// Construct with utility function: json5pp::object()
cout << m.is_object() << endl; // => 1
cout << m << endl; // => {"bar":123,"foo":"baz"}
// json5pp::value n{{"foo", 123}};
// Compile error (*1)
// (*1)
// These forms are rejected because an implicit conversion
// for arrays and objects makes ambiguousness, for example:
// json5pp::value x{{"foo", 123}}; // Ambiguous!
// // candidate: [["foo",123]]
// // candidate: {"foo":123}
//
// Use utility functions (json5pp::array(),json5pp::object()) to remove
// ambiguousness.
using namespace std;
json5pp::value x; // Default constructor makes null value
cout << x << endl; // => null
x = false; // Assign boolean with bool value
cout << x << endl; // => false
x = 123.45; // Assign number with double value
cout << x << endl; // => 123.45
x = 789; // Assign number with int value
cout << x << endl; // => 789
std::string str("bar");
x = str; // Assign string with const std::string& value
cout << x << endl; // => "bar"
// Because assignment copies the content of string,
// changing a source string does not affect `x`:
str += "123";
cout << x << endl; // => "bar" (Contents does NOT change)
x = "foo"; // Assign string with const char* value
cout << x << endl; // => "foo"
x = nullptr; // Assign null with nullptr_t value
cout << x << endl; // => null
x = json5pp::array({1});// Assign array with utility function: json5pp::array()
cout << x << endl; // => [1]
auto& a = x.as_array(); // You can get container object by as_array() method
// decltype(a) => std::vector<json5pp::value>&
a.push_back("foo"); // Add value at the end of array
cout << x << endl; // => [1,"foo"]
x = json5pp::object({{"foo","bar"}});
// Assign object with utility function: json5pp::object()
cout << x << endl; // => {"foo":"bar"}
// Note: Do not access `a` (array container) after replacing the content of `x`
// with a new object.
auto& o = x.as_object();// You can get container object by as_object() method
// decltype(o) => std::map<std::string, json5pp::value>&
// Note: Do not forget `&` when you use `auto` keyword!
o.emplace("baz", 123); // Add value with key "baz"
cout << x << endl; // => {"baz":123,"foo":"bar"}
json5pp::value y;
y = x; // You can copy the value by "=" operator
cout << y << endl; // => {"baz":123,"foo":"bar"}
// Because assignment arrays/objects is a deep copy,
// changing a source value `x` does not affect `y`:
o.erase("foo"); // Remove key "foo" from the content of `x` object
cout << x << endl; // => {"baz":123}
cout << y << endl; // => {"baz":123,"foo":"bar"} (Contents does NOT change)
using namespace std;
// Access boolean (See also: Truthy/falsy tests)
json5pp::value a(true);
auto a_value = a.as_boolean(); // decltype(a_value) => bool
cout << a_value << endl; // => 1
// Access number
json5pp::value b(123.45);
auto b_value1 = b.as_number(); // decltype(b_value1) => double
cout << b_value1 << endl; // => 123.45
auto b_value2 = b.as_integer(); // decltype(b_value2) => int
cout << b_value2 << endl; // => 123
// Access string
json5pp::value c("foo");
auto c_value = c.as_string(); // decltype(c_value) => std::string
cout << c_value << endl; // => foo
// Access array
json5pp::value d{1, "foo", false};
auto& d_value = d.as_array(); // decltype(d_value) => std::vector<json5pp::value>&
cout << d_value.size() << endl; // => 3
cout << d_value[0].as_number() << endl; // => 1
cout << d_value[1].as_string() << endl; // => foo
cout << d_value[2].as_boolean() << endl; // => 0
// Access array with indexer
cout << d[0].as_number() << endl; // => 1
cout << d[1].as_string() << endl; // => foo
cout << d[2].as_boolean() << endl; // => 0
// d[1] = 123; // Compile error (indexer is read-only)
// Access object
auto e = json5pp::object({{"bar", 123}, {"foo", true}});
auto& e_value = e.as_object(); // decltype(e_value) => std::map<std::string, json5pp::value>&
cout << e_value.size() << endl; // => 2
cout << e_value.at("bar").as_number() << endl; // => 123
cout << e_value.at("foo").as_boolean() << endl; // => 1
// Access object with indexer
cout << e["bar"].as_number() << endl; // => 123
cout << e["foo"].as_boolean() << endl; // => 1
// e["baz"] = 123; // Compile error (indexer is read-only)
// Invalid cast
// (If type does not match, as_xxx() method throws std::bad_cast())
json5pp::value f(123); // type is number
// f.as_null(); // throws std::bad_cast()
json5pp::value g(); // type is null
// f.as_boolean(); // throws std::bad_cast()
// Truthy/falsy test
// falsy: null, false, 0, -0, NaN, ""
// truthy: other values
json5pp::value truthy1(true);
json5pp::value truthy2(1);
json5pp::value truthy3("foo");
json5pp::value truthy4{};
auto truthy5 = json5pp::object({});
cout << (bool)truthy1 << endl; // => 1
cout << (bool)truthy2 << endl; // => 1
cout << (bool)truthy3 << endl; // => 1
cout << (bool)truthy4 << endl; // => 1
cout << (bool)truthy5 << endl; // => 1
json5pp::value falsy1(); // null
json5pp::value falsy2(false);
json5pp::value falsy3(0);
json5pp::value falsy4(numeric_limits<double>::quiet_NaN()); // NaN
json5pp::value falsy5(numeric_limits<double>::signaling_NaN()); // NaN
json5pp::value falsy6("");
cout << (bool)falsy1 << endl; // => 0
cout << (bool)falsy2 << endl; // => 0
cout << (bool)falsy3 << endl; // => 0
cout << (bool)falsy4 << endl; // => 0
cout << (bool)falsy5 << endl; // => 0
cout << (bool)falsy6 << endl; // => 0
using namespace std;
auto x = json5pp::parse("{\"foo\":[123,\"baz\"]}");
cout << x.is_object() << endl; // => 1
cout << x["foo"].is_array() << endl; // => 1
cout << x["foo"][0].as_number() << endl; // => 123
cout << x["foo"][1].as_string() << endl; // => baz
auto y = json5pp::parse5("{\"foo\"://this is comment\n[123,\"baz\"/*trailing comma-->*/,],}");
cout << y.is_object() << endl; // => 1
cout << y["foo"].is_array() << endl; // => 1
cout << y["foo"][0].as_number() << endl; // => 123
cout << y["foo"][1].as_string() << endl; // => baz
using namespace std;
// Make some example object...
json5pp::value x = json5pp::object({
{"foo", 123},
{"bar",
json5pp::array({
1, "baz", true,
})
},
});
// Stringify to output stream by "<<" operator
cout << x << endl;
// Stringify to std::string by stringify() method
auto s = x.stringify(); // decltype(s) => std::string
cout << s << endl; // => {"bar":[1,"baz",true],"foo":123}
// Stringify to output stream with indent specification
cout << json5pp::rule::space_indent<>() << x << endl; /* =>
{
"bar": [
1,
"baz",
true
],
"foo": 123
}
*/
// Stringify to std::string with indent specification
auto s2 = x.stringify(json5pp::rule::tab_indent<>());
cout << s2 << endl; /* =>
{
> "bar": [
> > 1,
> > "baz",
> > true,
> ],
> "foo": 123
}
(`>` means tab) */
- Not fully compatible with unquoted keys in JSON5 (Some unicode will be rejected as keys)
- All strings are assumed to be stored in UTF-8 encoding.
- More tests