JSON (ECMA-404 and JSON5) parser & stringifier written in C++11.
- Easy to embed to your project. Only single file
json5pp.hpprequired. - Parse standard JSON (ECMA-404) from
std::istreamorstd::string. - Parse JSON5 from
std::istreamorstd::string. - Stringify to
std::ostreamorstd::stringas standard JSON. - Stringify to
std::ostreamorstd::stringas 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. (xxxis one ofnull,boolean,number,string,arrayandobject) - Provides explicit cast to C++ type with
as_xxx()method. (xxxis one ofnull,boolean,number,integer,string,arrayandobject)- 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
finishis 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 JSON5std::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 indentjson5pp::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_commentandmulti_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_pointandtrailing_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).
Lmeans a number of tab (\t) characters for one-level indent.- If
Lis omitted, treat asL=1.
json5pp::rule::space_indent<L>()- Enable indent with space character(s).
Lmeans a number of space () characters for one-level indent.- If
Lis 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; // => 0using 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; // => bazusing 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