Yet another JSON parser, this time constexpr and without variants
github.com/beached/daw_json_linkPosted Apr 28, 2019, 4:40 PM UTC
Posted Apr 28, 2019, 4:40 PM UTC
I have started yet another JSON parsing/serializing library. I take the approach that we know what kind of data we are working with and do not use a tree of variant like types. One provides one or two methods that describe their data structure and map it to the JSON members. Also, it is constexpr, because all things need to be :)
With that, each mapped type has it's own custom JSON parser.
Interesting!
You say another, do you have other JSON parser projects? What makes them different?
I have another but I use it for my JSON to C++ interop code generator. It is like most others in that it builds a tree of variant like things. This project is different in that it directly constructs the C++ data type from the parser. For instance
struct A {
int a;
int b;
std::string c;
};
A{ int_parser( ), int_parser( ), string_parser( ) };
It is somewhat analogous to that. But with a bit more hierarchy thrown in and a few customization points. The description you provide is the type and order of the constructors arguments by default, or the callable Constructor that is passed to each
I have been doing something similar at work lately with https://github.com/nlohmann/json, except you do not have to define a whole new struct/class and give it methods.
You define a type like this:
constexpr char id[] = "id";
constexpr char value[] = "value";
using record_t = structure<
entry<id, std::string>,
entry<value, int>
>;
Which would translate into json like so:
{"id": "name", "value" : 12}
Then you can go ahead and start nesting that so it becomes:
constexpr char name[] = "name";
using record_list_t = structure<
entry<name, std::vector<record_t>,
entry<id, record_t>
...
>;//makes no sense just an example
And use it like so.
json j;
std::fstream{ "input.json", std::ios_base::in } >> j;
auto data = j.get<record_list_t >();
auto identifier = data.get<indexer<id>>();
The indexer could probably be omitted in another compiler but I couldn't make a method with const char* nttp work with MSVC so I left it like it was. If anyone's interested in how that's implemented I can probably share the code. With cnttp it will become much nicer to use though so yayy c++20 I guess.
I went wit her the return value so that I could use ADL and also not have to enter namespaces as much. Also by specifying what the JSON types are you get rid of a lot of branching but don’t limit the output types. If I am understanding you. I am assuming yours knows an int is integral and a JSON number
First of all, I'm not the implementer of that library, just my usage of it seemed similar to yours. The j.get<Ty>() that constructs the defined type actually calls a from_json(const json&, Ty&) overload that "you" have to define. What I've done is that I generate the structure and by extension it's from_json from the template parameters themselves rather than having to do the same boilerplate over and over.
So my code just converts this:
struct record_t {
std::string id;
int value;
};
void from_json(const json& j, record_t&r) {
j.at("id").get_to(r.id);
j.at("value").get_to(r.value);
}
Into the example shown above so:
constexpr char id[] = "id";
constexpr char value[] = "value";
using record_t = structure<
entry<id, std::string>,
entry<value, int>
>;
As I said I'm not implementing a library (nor do I aim to do so) and I kind of suspect the nlohmann/json uses something like a variant internally in its tree. Just the way I construct my structure from its tree seemed similar.
Ah I understand better now.
But yeah, C++20 will be so much cleaner for stuff like this. I am ready for it and used the non-type class template argument feature flag to enable using the string literals directly. Gcc9 is the only compiler I can find that supports it but it isn't really read(at least the version in Mac ports) as it cannot do debugging without the compiler crashing and the codegen is slow.
I believe that use of string literals got dropped from C++20.
Judging from this subreddit the natural order of learning to program is Hello World, FizzBuzz, followed by a JSON parser.
Hey if I was working to teach someone C++ it would be a nice choice or an intermediate project. It is a parser that is both useful and easy for many. You can gain competence in many parts of C++ and you can test it. One visit to json.org and one can get all the info to build the ebnf for it and build a rd parser.
This was somewhat an exercise in flipping the problem around a bit and using the type system to define the parts of the parser to use and where along with eliminating some branching with the assumption we know what types we are expecting. Additionally with that information we no longer need to store and traverse a tree of variant data structures, it is your reified class structure. With that it lended itself to iteration over parts or the whole of a json array and only fully parsing the parts one needed in the comfort of the std algorithms.