A killer modern C++ library for interacting with JSON.

Overview

JSON Voorhees

Yet another JSON library for C++. This one touts new C++11 features for developer-friendliness, an extremely slow-speed parser and no dependencies beyond a compliant compiler. If you love Doxygen, check out the documentation.

Features include (but are not necessarily limited to):

  • Simple
    • A value should not feel terribly different from a C++ Standard Library container
    • Write valid JSON with operator<<
    • Simple JSON parsing with parse
    • Reasonable error messages when parsing fails
    • Full support for Unicode-filled JSON (encoded in UTF-8 in C++)
  • Efficient
    • Minimal overhead to store values (a value is 16 bytes on a 64-bit platform)
    • No-throw move semantics wherever possible
  • Easy
    • Convert a value into a C++ type using extract<T>
    • Encode a C++ type into a value using to_json
  • Safe
    • In the best case, illegal code should fail to compile
    • An illegal action should throw an exception
    • Almost all utility functions have a strong exception guarantee
  • Stable
    • Worry less about upgrading -- the API and ABI will not change out from under you
  • Documented
    • Consumable by human beings
    • Answers questions you might actually ask
  • Compiler support
    • GCC (4.8+)
    • Clang++ (3.3+)

Build Status Coverage Status Flattr this git repo

JSON Conversions

Compile and Install

JSON Voorhees uses CMake as the automatic configuration software. On Linux or Mac OSX, if you have boost, cmake, g++ and make installed, simply:

$> cmake .
$> make
$> sudo make install

If you want to customize your compilation or installation, see the options in CMakeLists.txt for easy-to-use configuration options.

If you are on Windows, you can also use CMake if you want. However, it is probably easier to use the provided Visual Studio project files in msvc/vs2015 to get going. Hitting F5 should perform a NuGet restore, then compile and run all the unit tests.

Arch Linux

If you use Arch Linux, JSON Voorhees is easily installable via AUR. The "latest stable" is called json-voorhees, while there is a "close to tip" package called json-voorhees-git. With Arch, installation is as easy as yaourt json-voorhees!

Future

Future planned features can be found on the issue tracker.

License

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Miscellaneous

Compiler Support

  • Supported
    • GCC 4.8+
    • Clang 3.3+
  • Experimental
    • MSVC 14.0 CTP5+ (Visual C++ 2015)

Versioning

Like every software system ever, JSON Voorhees describes versions in 3 part semantic versioning: ${major}.${minor}.${patch}. The version components changing have very specific meanings:

  • major: There are no guarantees whatsoever across major releases.
  • minor: For a given minor release, all code is forward-compatible source-code compliant. That means code written against version 1.1 can be recompiled against version 1.4 and continue to work. No guarantees are made for going backwards (version 1.4 might have added new functions).
  • patch: Code is ABI-compatible across patch. Library A built against JSON Voorhees 1.2.4 should be able to pass a jsonv::value to library B built against JSON Voorhees 1.2.9. Any change to publicly-visible data structures or calling conventions will correspond to a bump in the minor version.

The preceding statements are not true if the version is suffixed with -preN. These values are "pre-release" and are allowed to do whatever they feel like before a release.

When developing code, follow this simple workflow to determine which version components need to change:

  1. Will this change force users to change their source code? If yes, bump the major version.
  2. Will this change force users to recompile to continue to work? If yes, bump the minor version.
  3. Will this change the behavior in any way? If yes, bump the patch version.
  4. Did I only change comments or rearrange code positioning (indentation, etc)? If yes, you do not need to update any part of the version.
  5. Did I miss something? Yes. Go back to #1 and try again.

Character Encoding

This library assumes you really love UTF-8. When parsing JSON, this library happily assumes all sequences of bytes from C++ are UTF-8 encoded strings; it assumes you also want UTF-8 encoded strings when converting from JSON in C++ land; and it assumes the non-ASCII contents of a source string should be treated as UTF-8. If you wish to use some other encoding format for your std::string, there is no convenient way to do that beyond conversion at every use site. There is a proposal to potentially address this.

On output, the system takes the "safe" route of using the numeric encoding for dealing with non-ASCII std::strings. For example, the std::string for "Travis Göckel" ("Travis G\xc3\xb6ckel") will be encoded in JSON as "Travis G\u00f6ckel", despite the fact that the character 'ö' is the most basic of the Unicode Basic Multilingual Planes. This is generally considered the most compatible option, as (hopefully) every transport mechanism can gracefully transmit ASCII character sequences without molestation. The drawback to this route is a needlessly lengthened resultant encoding if all components of the pipeline gracefully deal with UTF-8. There is an outstanding issue to address this shortcoming.

F.A.Q.

What makes JSON Voorhees different from the other C++ JSON libraries?

JSON Voorhees was written for a C++ programmer who wants to be productive in this modern world. What does that mean? There are a ton of JSON libraries floating around touting how they are "modern" C++ and so on. But who really cares? JSON Voorhees puts the focus more on the resulting C++ than any "modern" feature set. This means the library does not skip on string encoding details like having full support for UTF-8. Are there "modern" features? Sure, but this library is not meant to be a gallery of them -- a good API should get out of your way and let you work.

Another thing JSON Voorhees does not attempt to do is be a lightweight library; in fact, it wants to be the kitchen sink for anything you want to do in JSON in your C++ application. This means it is configurable for your needs. That said, the library does not depend on strange environment settings, so you can still drop the .cpp and .hpp files into your own project if you want.

It also includes a powerful serialization framework for converting from JSON into C++ types and back again. There is an extensible Serialization Builder DSL to help you writing your application. The serialization framework was designed with the modern application in mind -- you will like it or your money back!

Why are integer and decimal distinct types?

The JSON specification only has a number type, whereas this library has kind::integer and kind::decimal. Behavior between the two types should be fairly consistent -- comparisons between two different kinds should behave as you would expect (assuming you expect things like value(1) == value(1.0) and value(2.0) < value(10)). If you wish for behavior more like JavaScript, feel free to only use as_decimal().

The reason integer and decimal are not a single type is because of how people tend to use JSON in C++. If you look at projects that consume JSON, they make a distinction between integer and decimal values. Even in specification land, a distinction between the numeric type is pretty normal (for example: Swagger). To ultimately answer the question: integer and decimal are distinct types for the convenience of users.

Why are NaN and INFINITY serialized as a null?

The JSON specification does not have support for non-finite floating-point numbers like NaN and infinity. This means the value defined with object({ { "nan", std::nan("") }, { "infinity", INFINITY } }) to get serialized as { "nan": null, "infinity": null }. While this seems to constitute a loss of information, not doing this would lead to the encoder outputting invalid JSON text, which is completely unacceptable (unfortunately, this is a very common mistake in JSON libraries). If you want to check that there will be no information loss when encoding, use the utility funciton validate.

Why not throw when encoding? One could imagine the encoder::encode throwing something like an encode_error instead of outputting null to the stream. However, that would make operator<< a potentially-throwing operation, which is extremely uncommon and would be very surprizing (imagine if you tried to log a value and it threw).

Why not throw when constructing the value? Instead of waiting for encoding time to do anything about the problem, the library could attack the issue at the source and throw an exception if someone says value(INFINITY). This was not chosen as the behavior, because non-finite values are only an issue in the string representation, which is not a problem if the value is never encoded. You are free to use this JSON library without parse and encode, so it should not prevent an action simply because someone might use encode.

Why are there so many corruption checks?

If you are looking through the implementation, you will find a ton of places where there are default cases in switch statements that should be impossible to hit without memory corruption. This is because it is unclear what should happen when the library detects something like an invalid kind. The library could assert, but that seems overbearing when there is a reasonable option to fall back to. Alternatively, the library could throw in these cases, but that leads to innocuous-looking operations like x == y being able to throw, which is somewhat disconcerning.

This is really helpful! Can I give you money?

I would be Flattr-ed!

With such a cool name, do you have an equally cool logo?

Not really...

JSON: Serialized Killer

Issues
  • adding boost::optional support

    adding boost::optional support

    Hi Travis , I have started using your jsonv library in my new project https://github.com/venediktov/vanilla-rtb My repo is referencing your project via 'git subtree'

    my structures are rather complex but also require boost::optional which are not supported out of the box in your library, therefore I decided to make a pull request , please see incoming changes

    • adding specialization for boost::optional
    • fixing compilation on vc14 in jsonv-test
    • adding more file patterns to .gitignore

    in future I might be using http://en.cppreference.com/w/cpp/experimental/optional if I move it to C++17 instead of boost::optional ...

    I hope DSL from your package will work for my exchange_handlers like in my proof of concept use cases below. The idea is instead of marshalling manually I would have DSL files /classes that will do conversion from custom json formats into one class called openrtb::BidRequest , openrtb::BidResponse

    https://github.com/venediktov/cplusplus/blob/master/serialization_builder_tests.cpp https://github.com/venediktov/cplusplus/blob/master/openrtb.hpp

    If you are interested to contribute to my project I would be delighted !

    opened by venediktov 13
  • Should empty() and size() allow null values?

    Should empty() and size() allow null values?

    Currently, empty() and size() only allow objects, arrays, or strings, and throw a kind_error for any other type.

    Since a null value is by definition empty, and by definition has a size of 0, should empty() and size() do that instead of throwing when the kind is null?

    Or do other parts of jsonv rely on the current behaviour?

    opened by jjberry314 12
  • What is the inverse of traverse?

    What is the inverse of traverse?

    Use Case In web development, upon request, it is common to take a collection of paths and values and reconstruct an entire JSON tree. In web development, upon response, it is common to take a value and flatten it into a collection of paths and values.

    This is performed in PHP and in jQuery which adds to their success. In the case of jQuery this was a breaking change but they did it any way because it was so important. Many [C++] JSON libraries omit this capability.

    Your library does have traverse that could be used to turn a json object into a map<jsonv::path, jsonv::value>, though a convenience function to do this common use case would be appreciated. So the former use case is mostly taken care of; except for the preceding "." common to your paths.

    Now what about the former use case? What is the inverse of traverse function? value has the following value& at_path (const path &p) but it doesn't seem to have void set_value_at_path (const path &p, ?value?) "value & path (const path &p)" comes close but doesn't take an additional ?value? parameter. For convenience it would be great if there was a batch void paths|set_values_at_paths(std::map<path, value> values). It would be beneficial to perform this to an existing non primitive values or statically to create a new value.

    In both use cases strongly type [de]serialization to and from map<path,value> is also welcomed for convenience.

    Though at a minimal a set_value_at_path function is needed and all the others could be derived. If this can be performed now, please document how due to the importance of the proliferation of the web and its contribution to the popularity of JSON.

    enhancement 
    opened by jwaterloo 7
  • Parse null value in array

    Parse null value in array

    I have a JSON string like this: { "array" : [ null, null, { "name" : "toto" } ] } And i use DSL to parse it in struct

    jsonv::formats_builder()
        .type<cell>()
           .member("name", &cell::name)
        .type<my_array>()
            .member("array", &my_array::array)
            .register_container<std::vector<cell>>()
    

    But when i tried to parse this example, i have an error : jsonv::extraction_error: Extraction error at .array[0]: Unexpected type: expected object but found null. Can i have a solution to parse null value ?

    enhancement component-query 
    opened by ddway2 5
  • Segmentation fault if extracting jsonv::value that is not as serialization_builder expects

    Segmentation fault if extracting jsonv::value that is not as serialization_builder expects

    Hi,

    just starting to give JSON Voorhees a try... by trying an example based on the following link.

    This following causes a "Segmentation Fault" (64 bit Debian GNU/Linux)

    #include <iostream>
    #include <string>
    
    #include <jsonv/value.hpp>
    #include <jsonv/serialization_builder.hpp>
    #include <jsonv/parse.hpp>
    
    struct foo
    {
      int         a;
      int         b;
      std::string c;
    };
    struct bar
    {
      foo         x;
      foo         y;
      std::string z;
      std::string w;
    };
    
    std::ostream& operator<<(std::ostream& os, const foo &f)
    {
      os << " a: " << f.a << '\n'
         << " b: " << f.b << '\n'
         << " c: " << f.c << std::endl;
      return os;
    }
    
    std::ostream& operator<<(std::ostream& os, const bar &b)
    {
      os << "x:\n" << b.x << '\n'
         << "y:\n" << b.y << '\n'
         << "z: " << b.z << '\n'
         << "w: " << b.w << std::endl;
      return os;
    }
    
    
    int main()
    {
    
      jsonv::formats local_formats =
      jsonv::formats_builder()
      .type<foo>()
         .member("a", &foo::a)
         .member("b", &foo::b)
         .default_value(10)
         .member("c", &foo::c)
      .type<bar>()
         .member("x", &bar::x)
         .member("y", &bar::y)
         .member("z", &bar::z)
         .since(jsonv::version(2, 0))
         .member("w", &bar::w)
         .until(jsonv::version(5, 0))
      ;
    
    //      "aaaaa" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      std::string json_string = R"({
     "x": { "aaaaa": 50, "b": 20, "c": "Blah" }, 
     "y": { "a": 10,          "c": "No B?" },
     "z": "Only serialized in 2.0+",
     "w": "Only serialized before 5.0"
    }
    )";
    
    
      jsonv::formats format = jsonv::formats::compose({ jsonv::formats::defaults(), local_formats });
    
      jsonv::value val = jsonv::parse(json_string);
      bar x = jsonv::extract<bar>(val, format);
      std::cout << x << std::endl;
    
      return 0;
    }
    

    I'd expect some kind of exception or error handling here!

    The fix is

    //      "a" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      std::string json_string = R"({
     "x": { "a": 50, "b": 20, "c": "Blah" }, 
     "y": { "a": 10,          "c": "No B?" },
     "z": "Only serialized in 2.0+",
     "w": "Only serialized before 5.0"
    }
    )";
    
    

    Does this library have exceptions for parse errors? Can I print a nice message to the user explaining:

    • line number where error occurred
    • expected property ("a") and actual incorrectly supplied property ("aaaaa")

    Thanks

    bug 
    opened by ajneu 5
  • Don't consider unterminated strings as complete

    Don't consider unterminated strings as complete

    A parsed json string literal cannot be complete if the terminating quotation mark hasn't been seen yet.

    match_string() does consider such strings as potentially complete (match_result::complete_eof). This causes "\"hello"_json to compile without errors and "\""_json to throw std::range_error. Such incomplete strings should throw jsonv::parse_error instead.

    Returning match_result::incomplete_eof from match_string in the problematic corrects this.

    opened by sth 5
  • Added pkg-config and Debian packaging support

    Added pkg-config and Debian packaging support

    Hey Travis,

    working mostly on Debian systems, I hate unpackaged software installations so I thought it could be nice to add support for it.

    Hope you'll find this useful.

    opened by chybz 5
  • fix stack overflow

    fix stack overflow

    the size of allocated on the stack array to support T is too large , it causes stack overflow in template T extract(const value& from) const called recursively

    opened by venediktov 3
  • Linking with CMake is done in a completely incorrect way

    Linking with CMake is done in a completely incorrect way

    Sorry to burst the bubble of getting your latest release out on Friday the 13th, but I am having significant problems building and using jsonv on OSX (with multiple compilers) and lesser problems on multiple linux distros (again with multiple compilers). The issue is the dependency on Boost, and in particular, Boost locale.

    On a linux box, the boost libraries are typically named along the lines of libboost_locale.so. When I use jsonv in some application, all I need to do is to ensure I use -lboost_locale -lboost_filesystem -lboost_system when linking. The problem: I shouldn't have to do that. The jsonv library should make that dependency transparent. This is a small, easily solvable issue: Just add those -l<library_name> to my makefile.

    On a mac, there's no reason to need Boost locale. The LLVM compilers have long supported the header <codecvt>. The problem results from a test in src/jsonv/char_convert.cpp and from unexpected behavior from LLVM-based compilers. The LLVM compilers (clang et al.) define __GNUC__ (why do they do this?), and they make it look like the compiler in question is an ancient gcc compiler (and why do that do that???) Based on the preprocessor names, it appears that the compilers as released by Apple are gcc 4.1.2; they look like gcc 4.2.1 in the case of compilers from llvm.org. The problem is compounded by the way tools such as fink, macports, and homebrew mangle library names.

    The easiest solution (maybe not the best) is to add an else clause to the if(USE_BOOST_LOCALE) in CMakeLists.txt (git diff shown below):

    @@ -72,6 +72,8 @@ option(USE_BOOST_LOCALE
     if (USE_BOOST_LOCALE)
         add_definitions("-DJSONV_CHAR_CONVERT_USE_BOOST_LOCALE=1")
         set(REQUIRED_BOOST_LIBRARIES "locale" ${REQUIRED_BOOST_LIBRARIES})
    +else(USE_BOOST_LOCALE)
    +    add_definitions("-DJSONV_CHAR_CONVERT_USE_BOOST_LOCALE=0")
     endif(USE_BOOST_LOCALE)
    
    bug component-build 
    opened by DavidHammen 3
  • Force static linkage

    Force static linkage

    Is there a compile option to force static linkage rather than generating a shared object?

    Right now, the only way I can see to do it is to edit CMakeLists.txt line 132

    opened by jjberry314 2
  • Separate integer and decimal token types

    Separate integer and decimal token types

    The match_number function is fully aware that it saw a ., e, or E and is capable of emitting something more refined than a token_kind::number (token_kind::integer and token_kind::decimal). This will save parse_number from doing the double scan with characters.find_first_of(".eE"), which might help parsing performance.

    enhancement component-parsing 
    opened by tgockel 2
  • Investigate Bazel Build

    Investigate Bazel Build

    Bazel is generally a more friendly developer experience than the old-school CMake approach this library takes. Should look into building with Bazel.

    Open Questions

    • Can Bazel correctly generate SONAMEs? The current answer looks like "No", but this could be worked around.
    • Do pkg_deb and pkg_rpm effectively replace the CPack builder?
    opened by tgockel 0
  • value::insert is slow

    value::insert is slow

    Inserting into an object using val.insert({ std::move(key), std::move(val) }) is incredibly slow. While things should be moved everywhere, this does not appear to be happening. It is significantly faster to use val[std::move(key)] = std::move(val);

    bug enhancement component-value 
    opened by tgockel 0
  • Extract directly from text

    Extract directly from text

    Instead of only parsing into a jsonv::value, there should be a extractor which works directly on an AST index. This will allow extraction without the jsonv::value middleman.

    opened by tgockel 0
  • Don't rely on exceptions in `value::count_path`

    Don't rely on exceptions in `value::count_path`

    value::count_path relies on throwing std::out_of_range or kind_error to return 0 in cases where the provided path is not present or otherwise invalid. This is slower than needed. This should use the walk_path function directly instead.

    opened by tgockel 0
  • Investigate alternative to temporary buffers in char_convert

    Investigate alternative to temporary buffers in char_convert

    In detail/char_convert.cpp, the functions convert_to_wide and convert_to_narrow use temporary buffers (backed by alloca when available). This might be better replaced by a two-pass system instead of an allocation.

    enhancement question 
    opened by tgockel 0
Releases(v1.4.0)
Owner
Travis Gockel
Low-level distributed systems engineer.
Travis Gockel
https://github.com/json-c/json-c is the official code repository for json-c. See the wiki for release tarballs for download. API docs at http://json-c.github.io/json-c/

\mainpage json-c Overview and Build Status Building on Unix Prerequisites Build commands CMake options Testing Building with vcpkg Linking to libjson-

json-c 2.5k Aug 2, 2022
A C++ library for interacting with JSON.

JsonCpp JSON is a lightweight data-interchange format. It can represent numbers, strings, ordered sequences of values, and collections of name/value p

null 6.6k Aug 4, 2022
json-cpp is a C++11 JSON serialization library.

JSON parser and generator for C++ Version 0.1 alpha json-cpp is a C++11 JSON serialization library. Example #include <json-cpp.hpp> struct Foo {

Anatoly Scheglov 4 Dec 31, 2019
This is a JSON C++ library. It can write and read JSON files with ease and speed.

Json Box JSON (JavaScript Object Notation) is a lightweight data-interchange format. Json Box is a C++ library used to read and write JSON with ease a

Anhero inc. 108 Jul 7, 2022
A convenience C++ wrapper library for JSON-Glib providing friendly syntactic sugar for parsing JSON

This library is a wrapper for the json-glib library that aims to provide the user with a trivial alternative API to the API provided by the base json-

Rob J Meijer 16 May 10, 2022
json-build is a zero-allocation JSON serializer compatible with C89

json-build is a zero-allocation JSON serializer compatible with C89. It is inspired by jsmn, a minimalistic JSON tokenizer.

Lucas Müller 27 Jul 26, 2022
JSON for Modern C++

Design goals Sponsors Integration CMake Package Managers Pkg-config Examples JSON as first-class data type Serialization / Deserialization STL-like ac

Niels Lohmann 31.1k Jul 31, 2022
An easy-to-use and competitively fast JSON parsing library for C++17, forked from Bitcoin Cash Node's own UniValue library.

UniValue JSON Library for C++17 (and above) An easy-to-use and competitively fast JSON parsing library for C++17, forked from Bitcoin Cash Node's own

Calin Culianu 10 Aug 31, 2021
A Haskell library for fast decoding of JSON documents using the simdjson C++ library

hermes A Haskell interface over the simdjson C++ library for decoding JSON documents. Hermes, messenger of the gods, was the maternal great-grandfathe

Josh Miller 36 Jun 29, 2022
C library for encoding, decoding and manipulating JSON data

Jansson README Jansson is a C library for encoding, decoding and manipulating JSON data. Its main features and design principles are: Simple and intui

Petri Lehtinen 2.7k Aug 4, 2022
A very sane (header only) C++14 JSON library

JeayeSON - a very sane C++14 JSON library JeayeSON was designed out of frustration that there aren't many template-based approaches to handling JSON i

Jeaye Wilkerson 128 Jun 7, 2022
A tiny JSON library for C++11.

json11 json11 is a tiny JSON library for C++11, providing JSON parsing and serialization. The core object provided by the library is json11::Json. A J

Dropbox 2.4k Aug 6, 2022
a JSON parser and printer library in C. easy to integrate with any model.

libjson - simple and efficient json parser and printer in C Introduction libjson is a simple library without any dependancies to parse and pretty prin

Vincent Hanquez 262 Aug 6, 2022
Lightweight JSON library written in C.

About Parson is a lightweight json library written in C. Features Full JSON support Lightweight (only 2 files) Simple API Addressing json values with

Krzysztof Gabis 1.1k Jul 31, 2022
QJson is a qt-based library that maps JSON data to QVariant objects.

QJson JSON (JavaScript Object Notation) is a lightweight data-interchange format. It can represents integer, real number, string, an ordered sequence

Flavio Castelli 266 May 19, 2022
C++ header-only JSON library

Welcome to taoJSON taoJSON is a C++ header-only JSON library that provides a generic Value Class, uses Type Traits to interoperate with C++ types, use

The Art of C++ 466 Jul 19, 2022
A fast streaming JSON parsing library in C.

********************************************************************** This is YAJL 2. For the legacy version of YAJL see https

Lloyd Hilaiel 2.1k Jul 30, 2022
Very fast Python JSON parsing library

cysimdjson Fast JSON parsing library for Python, 7-12 times faster than standard Python JSON parser. It is Python bindings for the simdjson using Cyth

TeskaLabs 215 Aug 5, 2022
Immediate Mode JSON Serialization Library in C

JIM Immediate Mode JSON Serialization Library in C. Similar to imgui but for generating JSON. Example example.c: #include <stdio.h> #define JIM_IMPLE

Tsoding 30 Jul 28, 2022