Open source code learning: json11 source code reading

This is the second part of open source code learning. The previous one is: "Solve json parsing and encapsulation problems in one article, and take you to learn CJSON open source code"
This article reads the code of json11. Json11 is a JSON library written in C++11. Children interested in json11 code or use can read this article.

json11

json11 is a lightweight C++11 library that provides JSON serialization and deserialization functions,
When parsing json data, I tried several json libraries and finally decided to use json 11 for the following reasons:

  1. Common functions of json
  2. It can be used just like using C + + classes
  3. Chinese is not garbled, which is the main reason why I chose this library

github address of json11: json11.

json11 source code reading

Project structure

The project structure is relatively simple, with only two files

json11.hpp
json11.cpp

ReamME

JSON 11 is a lightweight C++11 library, which provides JSON serialization and deserialization functions
The core object is json11::Json. It can be used to represent any type of JSON data:
null, bool, number (int or double), string (std::string), array (std::vector), or object (std::map)

json11::Json objects, like other value types, support assignment, copy, transfer, comparison and other operations. We also provide auxiliary methods

Json::dump is used to serialize json11::Json type objects into string Json::parse
(static) used to deserialize std::string to an object of type json11::Json

Using the initializer provided by C++11, you can easily create a json11::Json object:

Json my_json = Json::object {
    { "key1", "value1" },
    { "key2", false },
    { "key3", Json::array { 1, 2, 3 } },
};
std::string json_str = my_json.dump();

Some built-in constructors are also provided here, which can automatically convert standard library types and user-defined types into json11::Json objects. For example:

class Point {
public:
    int x;
    int y;
    Point (int x, int y) : x(x), y(y) {}
    Json to_json() const { return Json::array { x, y }; }
};

std::vector<Point> points = { { 1, 2 }, { 10, 20 }, { 100, 200 } };
std::string points_json = Json(points).dump();
json11::

Json supports both subscript and keyword indexes:

Json json = Json::array { Json::object { { "k", "v" } } };
std::string str = json[0]["k"].string_value();

json11 design

It is mainly composed of the following categories

  • class Json
  • class JsonValue
  • class Value
  • class JsonInt JsonDouble ...

The Json class is an open interface with various construction methods.

class Json final { 
public
    // Types
    enum Type {
        NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
    };
    typedef std::vector<Json> array; //array and object are directly implemented using STL container
    typedef std::map<std::string, Json> object;

    Json() noexcept;                // NUL
    Json(std::nullptr_t) noexcept;  // NUL 
    Json(double value);             // NUMBER
    Json(int value);                // NUMBER
    Json(bool value);               // BOOL
    Json(const std::string &value); // STRING
    Json(std::string &&value);      // STRING
    Json(const char * value);       // STRING
    Json(const array &values);      // ARRAY
    Json(array &&values);           // ARRAY
    Json(const object &values);     // OBJECT
    Json(object &&values);          // OBJECT

    template <class T, class = decltype(&T::to_json)>
    Json(const T & t) : Json(t.to_json()) {}

    template <class M, typename std::enable_if<
        std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
        && std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
            int>::type = 0>
    Json(const M & m) : Json(object(m.begin(), m.end())) {}
    template <class V, typename std::enable_if<
        std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
            int>::type = 0>
    Json(const V & v) : Json(array(v.begin(), v.end())) {}
    Json(void *) = delete;

...

private:
    std::shared_ptr<JsonValue> m_ptr;

Several constructors are complex. See the following for details:

The entities of these data are stored in M_ The object pointed to by PTR.

class JsonValue {
protected:
    friend class Json;
    friend class JsonInt;
    friend class JsonDouble;
    virtual Json::Type type() const = 0;
    virtual bool equals(const JsonValue * other) const = 0;
    virtual bool less(const JsonValue * other) const = 0;
    virtual void dump(std::string &out) const = 0;
    virtual double number_value() const;
    virtual int int_value() const;
    virtual bool bool_value() const;
    virtual const std::string &string_value() const;
    virtual const Json::array &array_items() const;
    virtual const Json &operator[](size_t i) const;
    virtual const Json::object &object_items() const;
    virtual const Json &operator[](const std::string &key) const;
    virtual ~JsonValue() {}
};

JsonValue is an abstract base class that defines APIs for manipulating and accessing JSON objects. These APIs are not open to users for internal use. Users operate and access by the functions provided in the JSON class.
Next is its derived class Value:

template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:

    // Constructors
    explicit Value(const T &value) : m_value(value) {}
    explicit Value(T &&value)      : m_value(move(value)) {}

    // Get type tag
    Json::Type type() const override {
        return tag;
    }

    // Comparisons
    bool equals(const JsonValue * other) const override {
        return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
    }
    bool less(const JsonValue * other) const override {
        return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
    }

    const T m_value;
    void dump(string &out) const override { json11::dump(m_value, out); }
};

As a class template, Value instantiates different classes that hold the data entities of json objects.

JsonInt, JsonDouble and other classes can directly inherit the corresponding instantiated classes to save data.

Take JsonInt as an example:

class JsonInt final : public Value<Json::NUMBER, int> {
    double number_value() const override { return m_value; }
    int int_value() const override { return m_value; }
    bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
    bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }
public:
    explicit JsonInt(int value) : Value(value) {}
};

The inherited value is an int version, so you can save value directly through the parent constructor. It also implements some operations specific to this class, such as int_value() is used to get the value of value.

reference material

https://www.cnblogs.com/cknightx/p/7717539.html

Tags: C++ Rust

Posted on Thu, 14 Oct 2021 14:36:04 -0400 by wanted420