Skip to content

A modern C++, header-only library for JSON construction, with JSON Pointer, JSON Patch, JsonPath, CSV, MessagePack, CBOR

License

Notifications You must be signed in to change notification settings

xezon/jsoncons

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

Get jsoncons

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.

How to use it

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.

Extensions

Planned new features are listed on the roadmap

Benchmarks

json_benchmarks provides some measurements about how jsoncons compares to other json libraries. Results for JSONTestSuite and JSON_checker tests may be found here.

A simple program using jsoncons

#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

About jsoncons::basic_json

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

Features

By default, accepts and ignores C-style comments

    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

Validation without parse exceptions

    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

Range-based for loops with arrays

    json j = json::array{1,2,3,4};

    for (auto val : book.array_range())
    {
        std::cout << val << std::endl;
    }

Range-based for loops with objects

    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;
    }

_json and _ojson literal operators

    using namespace jsoncons::literals;


    ojson j2 = R"(
    {
        "StartDate" : "2017-03-01",
        "MaturityDate" : "2020-12-30"          
    }
    )"_ojson;

Multi-dimensional json arrays

    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

Merge key-value pairs from another json object

    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.

Convert json values from and to standard library types

    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

Convert from and to user defined types (and standard library containers of user defined types)

    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.

Serialize C++ objects directly to JSON formatted streams

#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

Dump json fragments into a larger document

#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"
    }
]

Filter json names and values

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.

Extensions

jsonpointer

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.

jsonpatch

#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.

jsonpath

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.

csv

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.

msgpack

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.

cbor

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.

Building the test suite and examples with CMake

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

Acknowledgements

Special thanks to our contributors

About

A modern C++, header-only library for JSON construction, with JSON Pointer, JSON Patch, JsonPath, CSV, MessagePack, CBOR

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C++ 99.1%
  • Other 0.9%