Giter Site home page Giter Site logo

beached / daw_json_link Goto Github PK

View Code? Open in Web Editor NEW
425.0 10.0 27.0 40.27 MB

Fast, convenient JSON serialization and parsing in C++

Home Page: https://beached.github.io/daw_json_link/

License: Boost Software License 1.0

CMake 4.32% C++ 95.61% C 0.07%
cpp cpp20 json cpp17 daw-json-link parse json-parser serialization constexpr parser

daw_json_link's Introduction

logo image

DAW JSON Link v3

Content

Intro

The DAW JSON Link library is a high performance, no allocation, C++ JSON Library supporting:

  • Direct parsing of JSON to your own data structures via simple declarative mappings
  • Earlier error reporting of unexpected data and errors
  • Low memory requirement, parser requires a small amount of stack but does not allocate itself.
  • Optimizations based on data types
  • Does not require owning the mapped types and is not intrusive

The library supports other parsing modes that can be mixed too.

  • Direct parsing into data structures
  • A lazy(PULL) JSON dom parser that allows for exploration of the document and selective parsing
  • A SAX(PUSH) event based parser for tasks like minimization
  • For large JSON array documents, one can use the json_array_iterator or json_array_range types.
  • For dynamic situations, a json_value type that allows iteration/lazy parsing of document

Some other notable features are:

  • Support for JSON tuple(heterogeneous arrays) and variant/tagged variant like mappings.
  • Automatic mapping of Containers, including the Map based Associative Structures
  • Able to parse to/from other numeric types like boost::multiprecision::cpp_int or GNU BigNum/Rational mpq_t
  • Optional Comment support
  • Trailing comma support
  • JSON Lines/NDJSON support with a thread partitioning helper
  • JSON Schema output support
  • Easy integration with reflection like libraries e.g Boost.Describe Support
  • JSON Path support
  • Minified and optional formatted output

The library is using the BSL license

When the structure of the JSON document is known, parsing is like the following:

MyThing thing = daw::json::from_json<MyThing>( json_string );

or for array documents, where the root of the document is an array, there is a helper method to make it easier, and it can be parsed like the following:

std::vector<MyThing> things = daw::json::from_json_array<MyThing>( json_string2 );

If the structure of the JSON document is unknown, one can construct a json_value that acts as a view and allows iteration and pull parsing on demand. The following is an example of opening a json_value from JSON data:

json_value val = daw::json::json_value( json_string );

The from_json and to_json methods allow access most of the parsing and serializing needs.

The event based parser(SAX) can be called via daw::json::json_event_parser. It takes two arguments, a json document and an event handler. The event handler can opt into events by having any of the following members:

  • handle_on_value
  • handle_on_array_start
  • handle_on_array_end
  • handle_on_class_start
  • handle_on_class_end
  • handle_on_number
  • handle_on_bool
  • handle_on_string
  • handle_on_null
  • handle_on_error

Code Examples

Mapping of your classes to JSON documents is done by specializing the trait daw::json::json_data_contract. A class that is mapped does not need to be mapped again if it is a member of another mapped class. There are two parts to the trait json_data_contract, first is a type alias named type that maps the JSON members to our class's constructor. This gets around needing private access to the class, assuming that data we would serialize would also be needed to construct the class. For example:

struct Thing {
 int a;
 int b;
};

The construct for Thing requires 2 integers and if we had the following JSON:

{
  "a": 42,
  "b": 1234
}

We could do the mapping like the following:

namespace daw::json {
  template<>
  struct json_data_contract<Thing> {
    static constexpr char const a[] = "a";
    static constexpr char const b[] = "b";
    using type = json_member_list<
      json_number<a, int>,
      json_number<b, int>
    >;
  };
}

This says that the JSON class, in the document, will have at least two members "a", and "b" that will be numbers that are integers. They will be passed to the constructor of Thing when daw::json::from_json<Thing>( json_doc ); is called, or that another class has a json_class<MemberName, Thing> member mapping. The above is the C++17 mapping method for the names, it works in future C++ versions too. But, in C++20 and later the names can be inline in the mapping e.g. json_number<"a", int>. The above is all that is needed for parsing JSON, for serializing a static member function is needed in the trait. Taking the previous example and extending it we could serialize Thing with:

namespace daw::json {
  template<>
  struct json_data_contract<Thing> {
    static constexpr char const a[] = "a";
    static constexpr char const b[] = "b";
    using type = json_member_list<
      json_number<a, int>,
      json_number<b, int>
    >;
  };
  
  static auto to_json_data( Thing const & v ) {
    return std::forward_as_tuple( v.a, v.b );
  }
}

The ordering of the members returned as a tuple need to match the mapping in the type alias type. This allows for passing the result of accessor methods too, if the data members are not public. Also, the class Thing must be constructible from int, int. The library supports both regular constructors and aggregate init ( Thing{ int, int } and Thing( int, int ) ) in C++17.

  • Note: The return type of to_json_data does not have to return a tuple of references to the existing object members, but can return calculated values too. It does not allow rvalues through as they are often temporaries, and it can result in long distance debugging. The library will static_assert on this and suggest including <daw/daw_tuple_forward.h> and calling daw::forward_nonrvalue_as_tuple( ... ) which store temporaries and forward other reference types.

The parsers work by constructing each argument in place in the call to the class's constructor. The individual argument parsers can be tuned for the specified circumstances of the data(e.g. floating point and integral numbers). Then with our type trait defining the arguments needed to construct the C++ class and their order we are able to look at each member in the JSON. Now we construct the value with the result of each parser; similar to T{ parse<0, json_string<"name">>( data ), parse<1, json_number<"age", unsigned>>( data ), parse<json_number<2, "number>>( data )}. For each member, the data stream will be moved forward until we find the member we need to parse, storing interested locations for later parsing. This process allows us to parse other classes as members too via the json_class<"member_name", Type> mapping type. So that each mapping trait only has to deal with its specific members and not their details. general parsing flow

Default mapping of types

In unnamed contexts, such as the root value, array elements, some key value types, and variant element lists where the name would be no_name, one can use some native C++ data types instead of the JSON mapping types. This includes, integer, floating point, bool, std::string, std::string_view, associative containers, sequence containers, Nullable/Optional like types and previously mapped classes.

For example, to map an array of string's.

template<>
struct daw::json::json_data_contract<MyType> {
  using type = json_member_list<json_array<"member_name", std::string>>;
};

Installing/Using

vcpkg

One can use vcpkg to grab the latest release, the port is called daw-json-link

Using in cmake

find_package( daw-json-link )
#...
target_link_libraries( MyTarget daw::daw-json-link )

As header only

The library is header only and can be cloned, along with it's two dependencies, followed by adding the include/ subfolders of each to the compiler's include path

Including in cmake project via FetchContent

To use daw_json_link in your cmake projects, adding the following should allow it to pull it in along with the dependencies:

include( FetchContent )
FetchContent_Declare(
  daw_json_link
  GIT_REPOSITORY https://github.com/beached/daw_json_link
  GIT_TAG release
)
FetchContent_MakeAvailable(daw_json_link)
#...
target_link_libraries( MyTarget daw::daw-json-link )

Installing

On a system with bash, it is similar on other systems too, the following can install for the system

git clone https://github.com/beached/daw_json_link
cd daw_json_link
mkdir build
cd build
cmake ..
cmake --install . 

This will allow for a cmake find_package install or using it as a regular header as long as the install prefix's include folder is included in the include paths of the compiler

Testing

The following will build and run the tests.

git clone https://github.com/beached/daw_json_link
cd daw_json_link
mkdir build
cd build
cmake -DDAW_ENABLE_TESTING=On ..
cmake --build . 
ctest .

After the build there the individual examples can be tested too. city_test_bin requires the path to the cities JSON file.

./tests/city_test_bin ../test_data/cities.json

Performance considerations

The order of the members in the data structures should generally match that of the JSON data, if possible. The parser is faster if it doesn't have to back track for values. Optional values, when missing in the JSON data, can slow down the parsing too. If possible have them sent as null. The parser does not allocate. The parsed to data types may and this allows one to use custom allocators or a mix as their data structures will do the allocation. The defaults for arrays is to use the std::vector and if this isn't desirable, you must supply the type.

Benchmarks

chart describing Kostya benchmark results

Escaping/Unescaping of member names

The library, currently, does not unescape/escape member names when serializing, they are expected to be valid and unescaped. This may be a future optional addition, as it does have a cost.

Differences between C++17 and C++20

There are slight differences between C++17 and C++20 where C++20 allows for some code not available in C++17.

Naming of JSON members

namespace daw::json {
  template<>
  struct json_data_contract<MyType> {
    static constexpr char const member_name[] = "memberName";
    using type = json_member_list<json_number<member_name>>;
  };
}

Both versions of C++ support this method for naming members.

C++ 20 Naming of JSON members

When compiled within C++20 compiler, in addition to passing a char const * as in C++17, the member names can be specified as string literals directly. C++20 compiler support is still really early and here be dragons. There are known issues with g++9.x in C++20 mode, and it's only tested with g++10/11. Here be dragons

namespace daw::json {
  template<>
  struct json_data_contract<MyType> {
    using type = json_member_list<json_number<"member_name">>;
  };
}

Using mapped data types

Once a data type has been mapped with a json_data_contract, the library provides methods to parse JSON to them

MyClass my_class = from_json<MyClass>( json_str );

Alternatively, if the input is trusted, the less checked version can be faster

MyClass my_class = from_json<MyClass, options::parse_flags<options::CheckedParseMode::no>>( json_str );

JSON documents with array root's use the from_json_array function to parse

std::vector<MyClass> my_data = from_json_array<MyClass>( json_str );

Alternatively, if the input is trusted, the less checked version can be faster

std::vector<MyClass> my_data = from_json_array<MyClass, std::vector<MyClass>, options::parse_flags<options::CheckedParseMode::no>>( json_str );

json_array_iterator

If you want to work from JSON array data you can get an iterator and use the std algorithms to Iterating over array's in JSON data can be done via the json_array_iterator

using iterator_t = json_array_iterator<MyClass>;
auto pos = std::find( iterator_t( json_str ), iterator_t( ), MyClass( ... ) );

Alternatively, if the input is trusted you can call the less checked version

using iterator_t = daw::json::json_array_iterator<MyClass, options::CheckedParseMode::no>;
auto pos = std::find( iterator_t( json_str ), iterator_t( ), MyClass( ... ) );

json_value

For a DOM like api, often used for things like GUI's and providing code when the mappings are inadequate, one can use json_value. This is used in the json_to_cpp tool.

auto jv = daw::json::json_value( json_doc );

One can use a JSON Path to extract an integer

int foo = as<int>( jv["path.to.int"] );

Here, "path.to.int" is a JSON Path that represents drilling into a JSON class like

{
  "path": {
    "to": {
      "int": 5
    }
  }
}

One can, also, select via an array like syntax in the JSON path too, "path[5]" would select the 5th element/member of "path". If you want to serialize to JSON. The JSON Path syntax works with from_json, from_json_array, and json_array_iterator too.

to_json

std::string my_json_data = to_json( MyClass{} );

Or serialize an array, collection, range, or view of things. Only requires std::begin(...) and std::end(...) to work for the type. This allows serialization when the type isn't a constructible collection of things.

std::vector<MyClass> arry = ...;
std::string my_json_data = to_json_array( arry );

Error Handling

Exceptions

Parsing errors default to throwing a daw::json::json_exception that includes information about the reason and location of failure.

-fno-exception

If exceptions are disabled the library will call std::terminate upon a parse error by default.

Custom Error Handling

While, the error handling defaults to throwing a daw::json::json_exception on errors, or calling std::terminate if exceptions are disabled. One can change this behaviour by setting the function pointer daw::json::daw_json_error_handler. The only requirement is that the function does not return. An example that utilizes this is in error_handling_bench_test.cpp

Parsing call

Error checking can be modified on a per-parse basis. from_json, from_json_array, json_value, json_array_iterator, and alike all support parsing options. calls can be supplied a Parser Option. The available options are documented in the parser_policies cookbook item.

Global

daw::json::json_exception has a member function std::string_view reason( ) const akin to std::exception's what( ) but returns a std::string with more context than what( ) does. If you want to disable exceptions in an environment that has them, you can define DAW_JSON_DONT_USE_EXCEPTIONS to disable exception throwing by the library or set the handler, this is no longer recommended as the handler can be set to one of the two defaults daw::json::default_error_handling_throwing or daw::json::default_error_handling_terminating.

Deserializing/Parsing

This can be accomplished by writing a specialization of json_data_contract in the daw::json namespace. For example:

#include <daw/json/daw_json_link.h>

#include <string>
#include <string_view>
#include <vector>

struct TestClass {
  int i = 0;
  double d = 0.0;
  bool b = false;
  std::string s{};
  std::vector<int> y{};

  TestClass(int Int, double Double, bool Bool, std::string S,
            std::vector<int> Y)
      : i(Int), d(Double), b(Bool), s(std::move( S ) ), y(std::move( Y )) {}
};

namespace daw::json {
template <> 
struct json_data_contract<TestClass> {
  using type =
    json_member_list<
      json_number<"i", int>, 
      json_number<"d">, 
      json_bool<"b">,
      json_string<"s">, 
      json_array<"y", int>
    >;
};
} // namespace daw::json

int main() {
  std::string_view test_001_t_json_data = R"({
    "i":5,
    "d":2.2e4,
    "b":false,
    "s":"hello world",
    "y":[1,2,3,4] 
    })";
  std::string_view json_array_data = R"([{
    "i":5,
    "d":2.2e4,
    "b":false,
    "s":"hello world",
    "y":[1,2,3,4] 
    },{
    "i":4,
    "d":122e4,
    "b":true,
    "s":"goodbye world",
    "y":[4,3,1,4] 
    }])";

  TestClass test_class = daw::json::from_json<TestClass>(test_001_t_json_data);
  std::vector<TestClass> arry_of_test_class =
      daw::json::from_json_array<TestClass>(test_001_t_json_data);
}

See on Compiler Explorer

Both aggregate and user constructors are supported. The description provides the values needed to construct your type and the order. The order specified is the order they are placed into the constructor. There are customization points to provide a way of constructing your type too. A class like:

#include <daw/json/daw_json_link.h>

struct AggClass {
  int a{};
  double b{};
};

namespace daw::json {
  template<>
  struct json_data_contract<AggClass> {
    using type = json_member_list<
      json_number<"a", int>,
      json_number<"b">
    >;
  };
}

Works too. Same but C++17

#include <daw/json/daw_json_link.h>

struct AggClass {
  int a{};
  double b{};
};

namespace daw::json {
  template<>
  struct json_data_contract<AggClass> {
    static inline constexpr char const a[] = "a";
    static inline constexpr char const b[] = "b";
    using type = json_member_list<
      json_number<a, int>,
      json_number<b>
    >;
  };
}

The class descriptions are recursive with their submembers. Using the previous AggClass one can include it as a member of another class

// See above for AggClass
struct MyClass {
  AggClass other;
  std::string_view some_name;
};

namespace daw::json {
  template<>
  struct json_data_contract<MyClass> {
    using type = json_member_list<
      json_class<"other", AggClass>,
      json_string<"id", std::string_view>
    >;
  };
}

The above maps a class MyClass that has another class that is described AggClass. Also, you can see that the member names of the C++ class do not have to match that of the mapped JSON names and that strings can use std::string_view as the result type. This is an important performance enhancement if you can guarantee the buffer containing the JSON file will exist as long as the class does.

Iterating over JSON arrays. The input iterator daw::json::json_array_iterator<JsonElement> allows one to iterator over the array of JSON elements. It is technically an input iterator but can be stored and reused like a forward iterator. It does not return a reference but a value.

#include <daw/json/daw_json_link.h>
#include <daw/json/daw_json_iterator.h>

#include <iostream>

struct AggClass {
  int a{};
  double b{};
};

namespace daw::json {
template<>
struct json_data_contract<AggClass> {
  using type = json_member_list<
    json_number<"a", int>,
    json_number<"b">
  >;
};
} // namespace daw::json

int main() {
  std::string json_array_data = R"([
    {"a":5,"b":2.2},
    {"a":5,"b":3.14},
    {"a":5,"b":0.122e44},
    {"a":5334,"b":34342.2}
     ])";
  using iterator_t = daw::json::json_array_iterator<AggClass>;
  auto pos =
      std::find_if(
        iterator_t(json_array_data),
        iterator_t(),
        [](AggClass const &element) { 
          return element.b > 1000.0;
        }
      );
  if(pos == iterator_t()) {
    std::cout << "Not found\n";
  } else {
    std::cout << "Found\n";
  }
}

Member Paths

Parsing can begin at a specific member or element. An optional member path to from_json, from_json_array, json_value, json_array_iterator, and alike can be specified. The format is a dot separated list of member names and optionally an array index such as member0.member1 which is like parsing from:

{
  "member0": {
    "member1": {}
  }
}

or member0[5].member1 which would start parsing at "member1" in a document like:

{
  "member0": [
    "a",
    "b",
    "c",
    "d",
    "e",
    {
      "member1": ""
    }
  ]
}

or

{
  "member0": {
    "a": "",
    "b": "",
    "c": "",
    "d": "",
    "e": "",
    "f": {
      "member1": ""
    }
  }
}

Comments

Comments are supported when the parser policy for them is used. Currently, there are two forms of comment policies.

  • C++ style // line comments and C style /* */ comments.
{ // This is a comment
    "a" /*this is also a comment*/: "a's value"
}
  • Hash style # line comments
{ # This is a comment
    "a" #this is also a comment
      : "a's value"
}

The comment policy can be set via PolicyCommentTypes. See parser_policies for more info.

Serialization

To enable serialization one must create an additional static function in your specialization of json_data_contract called to_json_data( Thing const & ); that returns a tuple of members. It will provide a mapping from your type to the arguments provided in the class description. To serialize to a JSON string, one calls to_json( my_thing ); where my_thing is a registered type or one of the fundamental types like Containers, Maps, Strings, bool, and numbers. The result of the to_json_data( Thing const & ) static method is a tuple who's elements match order in the accompanying json_data_contract type alias type. Because of the way the method is used, tuple's with rvalue elements will result in a use after destruction bug. The compiler will error if this happens. Including <daw/daw_tuple_forward.h> and the method daw::forward_nonrvalue_as_tuple instead will store the rvalues instead of passing them by reference. Often it is the result of calculated tuple elements. Using the example above lets add a to_json_data method

#include <daw/json/daw_json_link.h>
#include <tuple>

struct AggClass {
  int a;
  double b;
};

namespace daw::json {
  template<>
  struct json_data_contract<AggClass> {
    using type = json_member_list<
      json_number<"a", int>,
      json_number<"b">
    >;

    static constexpr auto to_json_data( AggClass const & value ) {
      return std::forward_as_tuple( value.a, value.b );
    }
  };
}
//...
AggData value = //...;
std::string test_001_t_json_data = to_json( value );

// or
std::vector<AggData> values = //...;
std::string json_array_data = to_json_array( values );

Alternatively, one can output to any WritableOutput type, by default this includes FILE*, iostreams, containers of Characters, and Character pointers. In your type's json_data_constract. Or if opted-into, one can get an ostream operator<< for their type that inserts the json into the output stream by adding a type alias named opt_into_iostreams the type it aliases doesn't matter, and include daw/json/daw_json_iostream.h . For example

#include <daw/json/daw_json_link.h>
#include <daw/json/daw_json_iostream.h>
#include <tuple>

struct AggClass {
  int a{};
  double b{};
};

namespace daw::json {
  template<>
  struct json_data_contract<AggClass> {
    using opt_into_iostreams = void;
    using type = json_member_list<
      json_number<"a", int>,
      json_number<"b">
    >;

    static inline auto to_json_data( AggClass const & value ) {
      return std::forward_as_tuple( value.a, value.b );
    }
  };
}
//...
AggData value = //...;
std::cout << value << '\n';

// or
std::vector<AggData> values = //...;
std::cout << values << '\n';

A working example can be found at daw_json_iostream_test.cpp or on compiler explorer

Common errors

  • error: pointer to subobject of string literal is not allowed in a template argument
    • Your compiler does not support Class Non-Type Template Parameters, or is not in C++20 mode. If you do not have compiler support, you can the C++17 naming style above e.g.
constexpr char const member_name[] = "member_name";
//...
json_link<member_name, Type>

Build configuration points

There are a few defines that affect how JSON Link operates

  • DAW_JSON_DONT_USE_EXCEPTIONS - Controls if exceptions are allowed. If they are not, a std::terminate() on errors will occur. This is automatic if exceptions are disabled(e.g -fno-exceptions)
  • DAW_ALLOW_SSE42 - Allow experimental SSE42 mode, generally the constexpr mode is faster
  • DAW_JSON_NO_CONST_EXPR - This can be used to allow classes without move/copy special members to be constructed from JSON data prior to C++ 20. This mode does not work in a constant expression prior to C++20 when this flag is no longer needed.

Requirements

  • C++ 17 compiler
  • GCC(9/10/11/12) have been tested
  • Clang(8/9/10/11/12/13/14) have been tested.
  • MSVC 19.29 has been tested.

Older compilers may still work but in testing some resulted in ICE's or compile errors due to buggy C++17 support. Often not using constexpr can help too.

For building tests

  • git
  • cmake
  • c++17 compiler

Contact

Limitations

  • When parsing classes, the default is unspecified when duplicate names are encounters as to which is used.
    One can guarantee that both can be parsed or order by using json_key_value parse type.
    When used with std::multimap<std::string, T> or std::vector<std::pair<std::string, T>> all members are preserved with the former in order. Alternatively, the json_value type will allow iteration over the class members and lazy parsing of the correct one. See Cookbook Key Values which demonstrates these methods.
  • Trailing commas, the parser makes no effort to detect trailing commas.

daw_json_link's People

Contributors

beached avatar codeinred avatar jk-jeon avatar kredegc avatar pauldreik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

daw_json_link's Issues

Look at working on freestanding C++

With a causal glance, most things are ok. Some things that might come up are
use of std::uint32_t/std::uint64_t to help with parsing, along with utf8 decoding. May have to require 32bit unsigned type

There are some helpers for std::optional/std::unique` pointer around nullable types. This may work better with a concept of sorts. But could be removed too.

Modernize Projects CMake

The first immediate error when trying to import this library is the use of CMAKE_SOURCE_DIR instead of CMAKE_CURRENT_SOURCE_DIR.

Second, the CMakeLists.txt doesn't seem to export anything at all so there is nothing to include.

As a sidenote, CMakeLists.txt seems to be in a very chaotic state, maybe it could use a clean-up anyway.

reordering a json may lead to "A value of known size was accessed past the end"

There seems to be some issue with json_custom types - or I am missing something. I wanted to try daw_json for the public data from binance.com . It seemed to work fine at first - but when I implemented the custom type Fixed8 (replaced by double in the code below) the order of the fields "a" and "b" in the json suddenly seemed to make a difference. When the order in the json matches the oder in the json_data_contract declaration it works fine, if the two arrays are swapped an exception is thrown instead.

The order of keys in the input are not meant to make a difference, right?

const std::string testJson = R"({"e":"depthUpdate","E":1609884707320,"s":"BTCBUSD","U":2544556159,"u":2544556219,"a":[["34198.19000000","0.00000000"],["34198.23000000","0.00000000"],["34198.25000000","0.00000000"],["34198.27000000","0.00000000"],["34198.30000000","0.00000000"],["34198.32000000","0.00958500"],["34198.40000000","0.01232200"],["34198.41000000","0.01000000"],["34198.87000000","0.00000000"],["34199.12000000","0.00000000"],["34199.16000000","0.00000000"],["34199.42000000","0.00000000"],["34200.25000000","0.00000000"],["34200.71000000","0.03199900"],["34201.27000000","0.03100000"],["34201.62000000","0.00000000"],["34202.58000000","0.00000000"],["34204.45000000","0.00952700"],["34207.64000000","0.00000000"],["34207.74000000","0.00000000"],["34209.77000000","0.00000000"],["34209.81000000","0.20400000"],["34225.94000000","0.20200000"],["34226.60000000","0.91050000"],["34236.08000000","0.30000000"]],"b":[["34198.31000000","0.00000000"],["34196.54000000","0.00453200"],["34193.34000000","0.00000000"],["34189.89000000","0.00000000"],["34188.82000000","0.00000000"],["34185.32000000","0.00000000"],["34184.84000000","0.06350200"],["34184.83000000","0.20000000"],["34180.61000000","0.08622700"],["34180.60000000","0.00000000"],["34180.59000000","0.19200000"],["34180.02000000","0.00000000"],["34180.01000000","0.00000000"],["34176.88000000","0.00000000"],["34166.48000000","0.00000000"],["34166.47000000","0.00000000"],["34159.85000000","0.03317500"],["34159.24000000","0.09394900"],["34158.29000000","1.00000000"],["34154.86000000","0.00000000"]]})";

struct Fixed8JsonConverter {
	double operator( )( std::string_view sv ) const {
		return stod(std::string(sv));
	}
	
	template<typename OutputIterator>
	constexpr OutputIterator operator()(OutputIterator _it, double _f) const {
		return daw::json::utils::copy_to_iterator(_it, std::to_string(_f));
	}
};

template<JSONNAMETYPE name>
using json_fixed8 = daw::json::json_custom<name, double, Fixed8JsonConverter, Fixed8JsonConverter>;

struct Change {
	double rate;
	double amount;
};

namespace daw::json {
	template<>
	struct json_data_contract<Change> {
		using type = json_ordered_member_list<
						json_fixed8<no_name>, 
						json_fixed8<no_name>
					>;
	};
}

class DepthUpdateJson {
public:
	int64 time; // E
	std::string pairName; // s
	int64 idTo; // u
	int64 idFrom; // U
	std::vector<Change> bid; // b
	std::vector<Change> ask; // a
};

namespace daw::json {
	template<>
	struct json_data_contract<DepthUpdateJson> {
		using type = json_member_list<
			json_number<"E", int64>,
			json_string<"s">,
			json_number<"u", int64>,
			json_number<"U", int64>,
			json_array<"b", Change>,
			json_array<"a", Change>
		>;
	};
}

int main (const int argC, char* argV[] ) {
	try {
		auto parsed = daw::json::from_json<DepthUpdateJson>(testJson);
	} catch (daw::json::json_exception e) {
		std::cout << "daw error: " << e.reason() << " near: '" << e.parse_location() << "'"  << std::endl;
	}
}

The code produces the output

daw error: A value of known size was accessed past the end near: '["34198.23000000","0.00000000"],["34198.25000000","0.00000000"],["34198.27000000","0.00000000"],["34198.30000000","0.00000000"],["34198.32000000","0.00958500"],["34198.40000000","0.01232200"],["34198.41000000","0.01000000"],["34198.87000000","0.00000000"],["34199.12000000","0.00000000"],["34199.16000000","0.00000000"],["34199.42000000","0.00000000"],["34200.25000000","0.00000000"],["34200.71000000","0.03199900"],["34201.27000000","0.03100000"],["34201.62000000","0.00000000"],["34202.58000000","0.00000000"],["34204.45000000","0.00952700"],["34207.64000000","0.00000000"],["34207.74000000","0.00000000"],["34209.77000000","0.00000000"],["34209.81000000","0.20400000"],["34225.94000000","0.20200000"],["34226.60000000","0.91050000"],["34236.08000000","0.30000000"]],"b":[["34198.31000000","0.00000000"],["34196.54000000","0.00453200"],["34193.34000000","0.00000000"],["34189.89000000","0.00000000"],["34188.82000000","0.00000000"],["34185.32000000","0.00000000"],["34184.84000000","0.06350200"],["34184.83000000","0.20000000"],["34180.61000000","0.08622700"],["34180.60000000","0.00000000"],["34180.59000000","0.19200000"],["34180.02000000","0.00000000"],["34180.01000000","0.00000000"],["34176.88000000","0.00000000"],["34166.48000000","0.00000000"],["34166.47000000","0.00000000"],["34159.85000000","0.03317500"],["34159.24000000","0.09394900"],["34158.29000000","1.00000000"],["34154.86000000","0.00000000"]]}'

Add function to get JSON stack from error

Currently the daw_json_exception has an optional pointer to the position of the parser where the error occurred. Using the sax interface to the parsing, build a stack trace to the position. This may require using both class_start/first from Range as errors can occur that cause the position to be null.

Rename Range/rng idiom

The Range name no longer fits as it is the general parse state now, not a range of characters

Move error handler into policy

Right now the error handling is a compile time option with either abort( ) or a json_exception being thrown. Make it a policy item so that the user can choose and mix.

‘const daw::json::json_name<1>’ is not derived from ‘const std::shared_ptr<_Tp>’

Hi again,

when trying to build my code, i got a really verbose error that I can't past entirely, it told me first some about pedantic (which i disabled), then i got a much bigger one that i paste here:

/usr/include/c++/9/bits/shared_ptr.h:388:5: note: template argument deduction/substitution failed: In file included from /home/tom/Projects/cproject/build/_deps/daw_json_link-src/include/daw/json/impl/daw_json_parse_policy.h:12, from /home/tom/Projects/cproject/build/_deps/daw_json_link-src/include/daw/json/impl/daw_json_iterator_range.h:11, from /home/tom/Projects/cproject/build/_deps/daw_json_link-src/include/daw/json/daw_json_link.h:11, from /home/tom/Projects/cproject/app/main.cpp:29: /home/tom/Projects/cproject/build/_deps/daw_json_link-src/include/daw/json/impl/daw_json_parse_common.h:265:56: note: ‘const string_view’ {aka ‘const daw::basic_string_view<char>’} is not derived from ‘const std::shared_ptr<_Tp>’ 265 | inline constexpr bool is_no_name = ( JsonMember::name == no_name ); | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ In file included from /usr/include/c++/9/memory:81, from /usr/local/include/boost/system/detail/std_interoperability.hpp:12, from /usr/local/include/boost/system/error_code.hpp:963, from /usr/local/include/boost/beast/core/error.hpp:14, from /usr/local/include/boost/beast/core/detail/bind_handler.hpp:13, from /usr/local/include/boost/beast/core/bind_handler.hpp:14, from /usr/local/include/boost/beast/core/async_base.hpp:14, from /usr/local/include/boost/beast/core.hpp:15, from /home/tom/Projects/cproject/app/main.cpp:19: /usr/include/c++/9/bits/shared_ptr.h:393:5: note: candidate: ‘template<class _Tp> bool std::operator==(std::nullptr_t, const std::shared_ptr<_Tp>&)’ 393 | operator==(nullptr_t, const shared_ptr<_Tp>& __a) noexcept | ^~~~~~~~

any idea what is happening here?

(here is my code)
`
struct WeatherUpdate
{
string type; // e

};

namespace daw::json
{
template <>
struct json_data_contract
{
using type = json_member_list<
json_string<"e">>;
};
} `

Rename JSON variable

Hello,
thank for the library which works perfectly, i was wondering :
is there anyway to map struct member to json property?
example:
json { ts: 45435235 } to struct { dateCreated: 45435235 }

Outdated(?) readme and missing checks in "checked" parsing

The Readme mentions a difference between checked (from_json) and unchecked (from_json_unchecked) calls https://github.com/beached/daw_json_link#using-mapped-data-types . The latter don't seem to exist (anymore?). Instead the unchecked version should probably be daw::json::from_json<MyClass, daw::json::NoCommentSkippingPolicyUnchecked>, right?

When playing around with this, I was very surprised to find, that the "checked" version does not in fact check whether the input is valid json at all. Unused invalid numbers (even very blatantly wrong numbers {"a":1.0fsdf3, ...}) and especially unterminated json is not reported if all required keys have been found before the end of the string. (A behavior I expected for the trusted / "unchecked" version, but not for the "checked" version)

Is there any way to add these checks or to have a FullyChecked version or something like that, that checks whether the input is valid json?

Some context: When receiving json via websockets we occasionally receive faulty json strings that consist of a valid json appended to the incomplete beginning of another one (see example below). They are very obviously invalid - but relying on daw jsons parsing we might miss these issues and treat them as valid instead.

Below code does not throw an exception:

std::string test = R"({"e":"aggTrade","E":1610729466077,"s":"BTCUSDT","a":516119850,"p":"36062.5500{"e":"aggTrade","E":1610732218225,"s":"BTCUSDT","a":516218642,"p":"35943.45000000","q":"0.08800100","f":574701726,"l":574701727,"T":1610732218224,"m":false,"M":true})";
std::cout << daw::json::from_json<int, daw::json::NoCommentSkippingPolicyChecked>(test, "a") << std::endl;

CBOR support

Any plan to support CBOR(RFC 8949)?

I think that if daw_json_link support CBOR, daw_json_link will be best C++ serialization/reflection library!!!

CMake install has an extra include directory

Currently the project is installed in /usr/local/include/include/daw/...

The error comes from,

install(DIRECTORY ${PROJECT_SOURCE_DIR}/include DESTINATION include)

When reading the docs... https://cmake.org/cmake/help/v3.13/command/install.html#directory

The directory structure is copied verbatim to the destination. The last component of each directory name is appended to the destination directory but a trailing slash may be used to avoid this because it leaves the last component empty.

CMake provides the example:

install(DIRECTORY icons scripts/ DESTINATION share/myproj
        [...])

will install the icons directory to share/myproj/icons and the scripts directory to share/myproj.

The code in questions adds an include folder to the include destination (this is a very confusing CMake feature 😕 )

issue with optional values?

Hi, just want to make sure i am not doing anything wrong

versions it happens on: (release and dev branches)
c++20 with g++ 10.2.1

struct test_complex works.
struct test_complex1 works.
struct test_complex2 fails to compile.

struct test_complex {
    std::vector<std::vector<double>> a;
};
namespace daw::json {
    template<>
    struct json_data_contract<test_complex>
    {
        using type = json_member_list<
            json_array<"a", std::vector<std::vector<double>> >
        >;
    };
}

struct test_complex1 {
        std::vector<std::vector<std::vector<double>>> a;
};
namespace daw::json {
    template<>
    struct json_data_contract<test_complex1>
    {
        using type = json_member_list<
            json_array<"a", std::vector<std::vector<std::vector<double>>> >
        >;
    };
}
struct test_complex2 {
        std::optional<std::vector<std::vector<double>>> a;
};
namespace daw::json {
    template<>
    struct json_data_contract<test_complex2>
    {
        using type = json_member_list<
            json_array_null<"a", std::optional<std::vector<std::vector<double>>> >
        >;
    };
}

the error i get is

response.hpp:299:75: error: no type named ‘parse_to_t’ in ‘std::conditional<false, std::optional<std::vector<std::vector<double> > >, daw::json::missing_json_data_contract_for<std::optional<std::vector<std::vector<double> > > > >::type’ {aka ‘struct daw::json::missing_json_data_contract_for<std::optional<std::vector<std::vector<double> > > >’}
  299 |      json_array_null<"a", std::optional<std::vector<std::vector<double>>> >
      |                                                                           ^
response.hpp:299:75: error: template argument 3 is invalid
response.hpp:299:75: error: template argument 4 is invalid
response.hpp:300:2: error: template argument 1 is invalid
  300 |  >;
      |  ^

Any ideas?

Explore a pull parser

Currently there is a push parser(the event driven one). This has an internal loop and state, events are handled by a callback handler. This can be extended such that the loop can be iterated by an external call to a next( ) method

tests: `make test` fails if `make check` was not run

Possibly a dependency-chain omission, but it was necessary to explicitly run make check in order for make test to work.

To reproduce, it was enough to run make clean and then make test will fail, as it does not find the necessary binaries to run.

Consider adopting fast_double_parser

I have no investigated the float parsing in daw_json_link, but I wanted to make sure you were aware that we have packaged the fast number parsing routine from the simdjson library into its own single-header library: https://github.com/lemire/fast_double_parser
This library provides exact parsing at high speed (under Linux, freeBSD, macOS, Visual Studio).

Feel free to close this issue if it is not relevant.

Installing

Hi everyone,
Sorry i'm quite new with C++ and i can't figure out how to install this library

i've tried
add_library(daw STATIC) target_include_directories(daw PUBLIC ${PROJECT_SOURCE_DIR}/external/daw) add_library(third_party STATIC) target_include_directories(third_party PUBLIC ${PROJECT_SOURCE_DIR}/external/third_party) target_link_libraries(${LIBRARY_NAME} PUBLIC doctest daw third_party)

but in my main.cpp i get this error when "make"
fatal error: daw/json/daw_json_link.h: No such file or directory 29 | #include "daw/json/daw_json_link.h"

Does anyone have any idea how to install it easily?

Allow filtering/transform of JSON

Allow one to take a JSON document and transform values. An interface can look like, but not exactly,

json_transform( json_doc, out_it, {
  { "path.to.member", []( json_value const & jv ) { ... } }, 
  { "path.to.array_item[1]", []( int const & i ) { return 2*i; }} } );

Allow for tagged_variant root class parsing

This is more generally said that a single sub member can describe the structure of the others.


struct ConfigV1 {
  string option1;
};

struct ConfigV2 {
  vector<string> option1;
};

The JSON document can have a mapped/non-mapped member that allows us to know which alternative to parse to.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.