jsoncons is a modern C++, header-only library for constructing JSON. It supports parsing a JSON file or string into a json
value, building a json
value in C++ code, and serializing a json
value to a file or string. It also provides an API for generating json read and write events in code, somewhat analogously to SAX processing in the XML world. It is distributed under the Boost Software License.
jsoncons uses some features that are new to C++ 11, including move semantics and the AllocatorAwareContainer concept. It has been tested with MS VC++ 2015, GCC 4.8, GCC 4.9, GCC 6.2.0 and recent versions of clang. Note that std::regex
isn't fully implemented in GCC 4.8., so jsoncons_ext/jsonpath
regular expression filters aren't supported for that compiler.
Download the latest release and unpack the zip file. Copy the directory include/jsoncons
to your include
directory. If you wish to use extensions, copy include/jsoncons_ext
as well.
Or, download the latest code on master.
- For a quick guide, see jsoncons: a C++ library for json construction.
- For the details, see the documentation.
As the jsoncons
library has evolved, names have sometimes changed. To ease transition, jsoncons deprecates the old names but continues to support many of them. See the deprecated list for the status of old names. The deprecated names can be suppressed by defining macro JSONCONS_NO_DEPRECATED
, which is recommended for new code.
- jsonpointer implements the IETF standard JavaScript Object Notation (JSON) Pointer
- jsonpatch implements the IETF standard JavaScript Object Notation (JSON) Patch
- jsonpath implements Stefan Goessner's JsonPath. It also supports search and replace using JsonPath expressions.
- csv implements reading (writing) JSON values from (to) CSV files
- msgpack implements encode to and decode from the MessagePack binary serialization format.
- cbor implements encode to and decode from the IETF standard Concise Binary Object Representation (CBOR).
Planned new features are listed on the roadmap
json_benchmarks provides some measurements about how jsoncons
compares to other json
libraries.
Results for JSONTestSuite and JSON_checker tests may be found here.
#include <iostream>
#include <fstream>
#include <jsoncons/json.hpp>
// For convenience
using jsoncons::json;
int main()
{
json color_spaces = json::array();
color_spaces.push_back("sRGB");
color_spaces.push_back("AdobeRGB");
color_spaces.push_back("ProPhoto RGB");
json image_sizing; // empty object
image_sizing["Resize To Fit"] = true; // a boolean
image_sizing["Resize Unit"] = "pixels"; // a string
image_sizing["Resize What"] = "long_edge"; // a string
image_sizing["Dimension 1"] = 9.84; // a double
json export_settings;
// create "File Format Options" as an object and put "Color Spaces" in it
export_settings["File Format Options"]["Color Spaces"] = std::move(color_spaces);
export_settings["Image Sizing"] = std::move(image_sizing);
// Write to stream
std::ofstream os("export_settings.json");
os << export_settings;
// Read from stream
std::ifstream is("export_settings.json");
json j = json::parse(is);
// Pretty print
std::cout << "(1)\n" << pretty_print(j) << "\n\n";
// Get reference to object member
const json& val = j["Image Sizing"];
// Access member as double
std::cout << "(2) " << "Dimension 1 = " << val["Dimension 1"].as<double>() << "\n\n";
// Try access member with default
std::cout << "(3) " << "Dimension 2 = " << val.get_with_default("Dimension 2",0.0) << "\n";
}
Output:
(1)
{
"File Format Options": {
"Color Spaces": ["sRGB","AdobeRGB","ProPhoto RGB"]
},
"Image Sizing": {
"Dimension 1": 9.84,
"Resize To Fit": true,
"Resize Unit": "pixels",
"Resize What": "long_edge"
}
}
(2) Dimension 1 = 9.84
(3) Dimension 2 = 0.0
The jsoncons library provides a basic_json
class template, which is the generalization of a json
value for different character types, different policies for ordering name-value pairs, etc.
typedef basic_json<char,
ImplementationPolicy = sorted_policy,
Allocator = std::allocator<char>> json;
The library includes four instantiations of basic_json
:
-
json constructs a utf8 character json value that sorts name-value members alphabetically
-
ojson constructs a utf8 character json value that preserves the original name-value insertion order
-
wjson constructs a wide character json value that sorts name-value members alphabetically
-
wojson constructs a wide character json value that preserves the original name-value insertion order
std::string s = R"(
{
// Single line comments
/*
Multi line comments
*/
}
)";
// Default
json j = json::parse(s);
std::cout << "(1) " << j << std::endl;
// Strict
try
{
strict_parse_error_handler err_handler;
json j = json::parse(s, err_handler);
}
catch (const parse_error& e)
{
std::cout << "(2) " << e.what() << std::endl;
}
Output:
(1) {}
(2) Illegal comment at line 3 and column 10
std::string s = R"(
{
"StartDate" : "2017-03-01",
"MaturityDate" "2020-12-30"
}
)";
std::stringstream is(s);
json_reader reader(is);
std::error_code ec;
reader.read(ec);
if (ec)
{
std::cout << ec.message()
<< " on line " << reader.line_number()
<< " and column " << reader.column_number()
<< std::endl;
}
Output:
Expected name separator ':' on line 4 and column 20
json j = json::array{1,2,3,4};
for (auto val : book.array_range())
{
std::cout << val << std::endl;
}
json book = json::object{
{"author", "Haruki Murakami"},
{"title", "Kafka on the Shore"},
{"price", 25.17}
};
for (const auto& kv : book.object_range())
{
std::cout << kv.key() << "="
<< kv.value() << std::endl;
}
using namespace jsoncons::literals;
ojson j2 = R"(
{
"StartDate" : "2017-03-01",
"MaturityDate" : "2020-12-30"
}
)"_ojson;
json a = json::make_array<3>(4, 3, 2, 0.0);
double val = 1.0;
for (size_t i = 0; i < a.size(); ++i)
{
for (size_t j = 0; j < a[i].size(); ++j)
{
for (size_t k = 0; k < a[i][j].size(); ++k)
{
a[i][j][k] = val;
val += 1.0;
}
}
}
std::cout << pretty_print(a) << std::endl;
Output:
[
[
[1.0,2.0],
[3.0,4.0],
[5.0,6.0]
],
[
[7.0,8.0],
[9.0,10.0],
[11.0,12.0]
],
[
[13.0,14.0],
[15.0,16.0],
[17.0,18.0]
],
[
[19.0,20.0],
[21.0,22.0],
[23.0,24.0]
]
]
See json::make_array for details
json j = json::parse(R"(
{
"a" : "1",
"b" : [1,2,3]
}
)");
json source = json::parse(R"(
{
"a" : "2",
"c" : [4,5,6]
}
)");
j.merge(std::move(source));
std::cout << pretty_print(j) << std::endl;
Output:
{
"a": "1",
"b": [1,2,3],
"c": [4,5,6]
}
See json::merge and json::merge_or_update for details.
std::vector<int> v{1, 2, 3, 4};
json j(v);
std::cout << "(1) "<< j << std::endl;
std::deque<int> d = j.as<std::deque<int>>();
std::map<std::string,int> m{{"one",1},{"two",2},{"three",3}};
json j(m);
std::cout << "(2) " << j << std::endl;
std::unordered_map<std::string,int> um = j.as<std::unordered_map<std::string,int>>();
Output:
(1) [1,2,3,4]
(2) {"one":1,"three":3,"two":2}
See json_type_traits
struct book
{
std::string author;
std::string title;
double price;
};
namespace jsoncons
{
template<class Json>
struct json_type_traits<Json, book>
{
// Implement static functions is, as and to_json
};
}
book book1{"Haruki Murakami", "Kafka on the Shore", 25.17};
book book2{"Charles Bukowski", "Women: A Novel", 12.0};
std::vector<book> v{book1, book2};
json j = v;
std::list<book> l = j.as<std::list<book>>();
See Type Extensibility for details.
#include <iostream>
#include <map>
#include <tuple>
#include <jsoncons/serialization_traits.hpp>
using namespace jsoncons;
int main()
{
std::map<std::string,std::tuple<std::string,std::string,double>> employees =
{
{"John Smith",{"Hourly","Software Engineer",10000}},
{"Jane Doe",{"Commission","Sales",20000}}
};
std::cout << "(1)\n" << std::endl;
dump(employees,std::cout);
std::cout << "\n\n";
std::cout << "(2) Again, with pretty print\n" << std::endl;
dump(employees,std::cout,true);
}
Output:
(1)
{"Jane Doe":["Commission","Sales",20000.0],"John Smith":["Hourly","Software Engineer",10000.0]}
(2) Again, with pretty print
{
"Jane Doe": ["Commission","Sales",20000.0],
"John Smith": ["Hourly","Software Engineer",10000.0]
}
See dump
#include <jsoncons/json.hpp>
using namespace jsoncons;
int main()
{
const json some_books = json::parse(R"(
[
{
"title" : "Kafka on the Shore",
"author" : "Haruki Murakami",
"price" : 25.17
},
{
"title" : "Women: A Novel",
"author" : "Charles Bukowski",
"price" : 12.00
}
]
)");
const json more_books = json::parse(R"(
[
{
"title" : "A Wild Sheep Chase: A Novel",
"author" : "Haruki Murakami",
"price" : 9.01
},
{
"title" : "Cutter's Way",
"author" : "Ivan Passer",
"price" : 8.00
}
]
)");
json_serializer serializer(std::cout, true); // pretty print
serializer.begin_json();
serializer.begin_array();
for (const auto& book : some_books.array_range())
{
book.dump_fragment(serializer);
}
for (const auto& book : more_books.array_range())
{
book.dump_fragment(serializer);
}
serializer.end_array();
serializer.end_json();
}
Output:
[
{
"author": "Haruki Murakami",
"price": 25.17,
"title": "Kafka on the Shore"
},
{
"author": "Charles Bukowski",
"price": 12.0,
"title": "Women: A Novel"
},
{
"author": "Haruki Murakami",
"price": 9.01,
"title": "A Wild Sheep Chase: A Novel"
},
{
"author": "Ivan Passer",
"price": 8.0,
"title": "Cutter's Way"
}
]
You can rename object members with the built in filter rename_object_member_filter
#include <sstream>
#include <jsoncons/json.hpp>
#include <jsoncons/json_filter.hpp>
using namespace jsoncons;
int main()
{
std::string s = R"({"first":1,"second":2,"fourth":3,"fifth":4})";
json_serializer serializer(std::cout);
// Filters can be chained
rename_object_member_filter filter2("fifth", "fourth", serializer);
rename_object_member_filter filter1("fourth", "third", filter2);
// A filter can be passed to any function that takes
// a json_input_handler ...
std::cout << "(1) ";
std::istringstream is(s);
json_reader reader(is, filter1);
reader.read();
std::cout << std::endl;
// or a json_output_handler
std::cout << "(2) ";
ojson j = ojson::parse(s);
j.dump(filter1);
std::cout << std::endl;
}
Output:
(1) {"first":1,"second":2,"third":3,"fourth":4}
(2) {"first":1,"second":2,"third":3,"fourth":4}
Or define and use your own filters. See json_filter for details.
Example. Select author from second book
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpointer/jsonpointer.hpp>
using namespace jsoncons;
int main()
{
json root = json::parse(R"(
[
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
}
]
)");
json result;
jsonpointer::jsonpointer_errc ec;
std::tie(result,ec) = jsonpointer::get(root, "/1/author");
if (ec == jsonpointer::jsonpointer_errc())
{
std::cout << result << std::endl;
}
}
Output:
"Evelyn Waugh"
jsonpointer::get may also be used to query packed cbor values.
See jsonpointer for details.
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpatch/jsonpatch.hpp>
using namespace jsoncons;
using namespace jsoncons::literals;
int main()
{
// Apply a JSON Patch
json doc = R"(
{ "foo": "bar"}
)"_json;
json doc2 = doc;
json patch = R"(
[
{ "op": "add", "path": "/baz", "value": "qux" },
{ "op": "add", "path": "/foo", "value": [ "bar", "baz" ] }
]
)"_json;
jsonpatch::jsonpatch_errc ec;
std::string path;
std::tie(ec,path) = jsonpatch::patch(doc,patch);
std::cout << "(1)\n" << pretty_print(doc) << std::endl;
// Create a JSON Patch
auto patch2 = jsonpatch::diff(doc2,doc);
std::cout << "(2)\n" << pretty_print(patch2) << std::endl;
std::tie(ec,path) = jsonpatch::patch(doc2,patch2);
std::cout << "(3)\n" << pretty_print(doc2) << std::endl;
}
Output:
(1)
{
"baz": "qux",
"foo": ["bar","baz"]
}
(2)
[
{
"op": "replace",
"path": "/foo",
"value": ["bar","baz"]
},
{
"op": "add",
"path": "/baz",
"value": "qux"
}
]
(3)
{
"baz": "qux",
"foo": ["bar","baz"]
}
See jsonpatch for details.
Example file (booklist.json):
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
}
]
}
}
#include <jsoncons/json.hpp>
#include <jsoncons_ext/jsonpath/json_query.hpp>
using namespace jsoncons;
using namespace jsoncons::jsonpath;
int main()
{
std::ifstream is("input/booklist.json");
json booklist;
is >> booklist;
// All books whose author's name starts with Evelyn
json result1 = json_query(booklist, "$.store.book[?(@.author =~ /Evelyn.*?/)]");
std::cout << "(1)\n" << pretty_print(result1) << std::endl;
// Normalized path expressions
json result2 = json_query(booklist, "$.store.book[?(@.author =~ /Evelyn.*?/)]",
result_type::path);
std::cout << "(2)\n" << pretty_print(result2) << std::endl;
// Change the price of "Moby Dick"
json_replace(booklist,"$.store.book[?(@.isbn == '0-553-21311-3')].price",10.0);
std::cout << "(3)\n" << pretty_print(booklist) << std::endl;
}
Output:
(1)
[
{
"author": "Evelyn Waugh",
"category": "fiction",
"price": 12.99,
"title": "Sword of Honour"
}
]
(2)
[
"$['store']['book'][1]"
]
(3)
{
"store": {
"book": [
{
"author": "Nigel Rees",
"category": "reference",
"price": 8.95,
"title": "Sayings of the Century"
},
{
"author": "Evelyn Waugh",
"category": "fiction",
"price": 12.99,
"title": "Sword of Honour"
},
{
"author": "Herman Melville",
"category": "fiction",
"isbn": "0-553-21311-3",
"price": 10.0,
"title": "Moby Dick"
}
]
}
}
See jsonpath for details.
Example file (tasks.csv)
project_id, task_name, task_start, task_finish
4001,task1,01/01/2003,01/31/2003
4001,task2,02/01/2003,02/28/2003
4001,task3,03/01/2003,03/31/2003
4002,task1,04/01/2003,04/30/2003
4002,task2,05/01/2003,
#include <fstream>
#include <jsoncons/json.hpp>
#include <jsoncons_ext/csv/csv_reader.hpp>
#include <jsoncons_ext/csv/csv_serializer.hpp>
using namespace jsoncons;
using namespace jsoncons::csv;
int main()
{
std::ifstream is("input/tasks.csv");
csv_parameters params;
params.assume_header(true)
.trim(true)
.ignore_empty_values(true)
.column_types("integer,string,string,string");
ojson tasks = decode_csv<ojson>(is, params);
std::cout << "(1)\n" << pretty_print(tasks) << "\n\n";
std::cout << "(2)\n";
encode_csv(tasks, std::cout);
}
Output:
(1)
[
{
"project_id": 4001,
"task_name": "task1",
"task_start": "01/01/2003",
"task_finish": "01/31/2003"
},
{
"project_id": 4001,
"task_name": "task2",
"task_start": "02/01/2003",
"task_finish": "02/28/2003"
},
{
"project_id": 4001,
"task_name": "task3",
"task_start": "03/01/2003",
"task_finish": "03/31/2003"
},
{
"project_id": 4002,
"task_name": "task1",
"task_start": "04/01/2003",
"task_finish": "04/30/2003"
},
{
"project_id": 4002,
"task_name": "task2",
"task_start": "05/01/2003"
}
]
(2)
project_id,task_name,task_start,task_finish
4001,task2,02/01/2003,02/28/2003
4001,task3,03/01/2003,03/31/2003
4002,task1,04/01/2003,04/30/2003
4002,task2,05/01/2003,
See csv for details.
The msgpack
extension supports encoding json to and decoding from the MessagePack binary serialization format.
Example file (book.json):
[
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
}
]
#include <jsoncons/json.hpp>
#include <jsoncons_ext/msgpack/msgpack.hpp>
using namespace jsoncons;
using namespace jsoncons::msgpack;
int main()
{
std::ifstream is("input/book.json");
ojson j1;
is >> j1;
// Encode ojson to MessagePack
std::vector<uint8_t> v = encode_msgpack(j1);
// Decode MessagePack to ojson
ojson j2 = decode_msgpack<ojson>(v);
std::cout << pretty_print(j2) << std::endl;
// or to json (now alphabetically sorted)
json j3 = decode_msgpack<json>(v);
// or to wjson (converts from utf8 to wide characters)
wjson j4 = decode_msgpack<wjson>(v);
}
Output:
[
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
}
]
See msgpack for details.
The cbor
extension supports encoding json to and decoding from the cbor binary serialization format.
This example illustrates encoding a Reputation Interchange data object to and from cbor.
#include <jsoncons/json.hpp>
#include <jsoncons_ext/cbor/cbor.hpp>
using namespace jsoncons;
using namespace jsoncons::cbor;
int main()
{
ojson j1 = ojson::parse(R"(
{
"application": "hiking",
"reputons": [
{
"rater": "HikingAsylum.example.com",
"assertion": "is-good",
"rated": "sk",
"rating": 0.90
}
]
}
)");
std::vector<uint8_t> v = encode_cbor(j1);
ojson j2 = decode_cbor<ojson>(v);
std::cout << pretty_print(j2) << std::endl;
}
Output:
{
"application": "hiking",
"reputons": [
{
"rater": "HikingAsylum.example.com",
"assertion": "is-good",
"rated": "sk",
"rating": 0.9
}
]
}
jsonpointer::get may be used to query packed cbor values.
See cbor for details.
CMake is a cross-platform build tool that generates makefiles and solutions for the compiler environment of your choice. On Windows you can download a Windows Installer package. On Linux it is usually available as a package, e.g., on Ubuntu,
sudo apt-get install cmake
Instructions for building the test suite with CMake may be found in
jsoncons/tests/build/cmake/README.txt
Instructions for building the examples with CMake may be found in
jsoncons/examples/build/cmake/README.txt
Special thanks to our contributors