JSON for Modern C++(原 nlohmann json)再封装之toJson

JSON for Modern C++ 可以说是非常摩登的一个C++ json 库了,支持容器化操作(push_back等操作),支持从 stl 容器(vector ,map)创建 json。具体用法就不赘述了,打开文章开头的链接自行查看即可,非常简单。

这篇文章主要是通过自定义树形结构,对这个库进行进一步封装,以期待实现动态的 json 序列化和反序列化。

树形结构擅于组织具有节点关系的动态数据,也适合递归,和 json 的结构不能说很像,只能说非常适合json,所以用它来做与 json 交互的数据结构。

首先创建一个树形结构的基本节点,作为所有节点对象的父类。由于动态树形对象的析构,需要进行链式动态析构(父节点delete子节点),如果在栈上申请对象的话是不行的,所以把析构函数放到 protected 下,这样就不能在栈上创建这个对象了。所有继承这个节点的类,都要把析构函数放到 protected 下。

#include <iostream>
#include <nlohmann/json.hpp>

using namespace nlohmann;

class Node {
protected:
    //节点名称
    std::string nodeName;
    //本节点对应的json
    json j;
    //子节点们
    std::vector<Node*> nodes;
public:
    //把本类序列化成json的方法,由子类实现
    virtual void toJson(json& j, std::string& errMsg) {

    }
    //把json反序列化成本类的方法,由子类实现
    virtual void toNode(json& j, std::string& errMsg) {

    }

    //添加子节点
    void addNode(Node* node) {
        nodes.push_back(node);
    }

    //把子节点序列化成json
    void childNodesToJson(json& j) {
        for (Node* node : nodes) {
            json jnode;
            std::string err;
            node->toJson(jnode, err);
            j[node->nodeName] = jnode;
        }
    }

    //创建一个子节点类型的节点
    template<typename T>
    static T* create() {
        return new T;
    }

    //delete本节点
    template<typename T>
    T* release() {
        delete this;
        return nullptr;
    }


    void setNodeName(std::string nodeName) {
        this->nodeName = nodeName;
    }

protected:
    Node() {

    }
    //由于析构时需要动态的,链式的析构,所以不允许在栈上申请对象
    virtual ~Node() {
        for (Node* node : nodes) {
            node->release<Node>();
        }
    }
};

为了方便,再写一个代表节点数组的类 NodeArray,来表示json中的数组结构,它既是 Node* 的数组,本身又是一个 Node*

class NodeArray :public std::vector<Node*>, public Node {
public:
    NodeArray(std::string name) {
        nodeName = name;
    }
    NodeArray() {}
    void toJson(json& j, std::string& errMsg) override{
        int size = this->size();
        
        for (int i = 0; i < size; i++) {
            json jchild;
            (*this)[i]->toJson(jchild, errMsg);
            j.push_back(jchild);
        }
    }
    void toNode(json& j, std::string& errMsg) override {

    }
protected:
    ~NodeArray() {
        for (Node* node : *this) {
            node->release<Node>();
        }
    }
};

有了这两个基本的类,就可以进行子类化开发了,比如有这样一个json,里面嵌套了一个数组

    "ship": {
        "cargo": [
            {
                "good": "FUEL",
                "quantity": 23,
                "totalVolume": 23
            },
            {
                "good": "FUEL",
                "quantity": 23,
                "totalVolume": 23
            },
            {
                "good": "FUEL",
                "quantity": 23,
                "totalVolume": 23
            }
        ],
        "class": "MK-I",
        "id": "ckon84fo20196vinzktdlvdlv",
        "location": "OE-PM-TR",
        "manufacturer": "Jackshaw",
        "maxCargo": 50,
        "plating": 5,
        "spaceAvailable": 27,
        "speed": 1,
        "type": "JW-MK-I",
        "weapons": 5,
        "x": 21,
        "y": -24
    }

为了生成一个形如这样的json,来子类化 Node,首先创建一个描述数组中的元素

            {
                "good": "FUEL",
                "quantity": 23,
                "totalVolume": 23
            },

的类,代码如下:

class cargo :public Node {
public:
    std::string good = "FUEL";
    int quantity = 17;
    int totalVolume = 17;
    void toJson(json& j, std::string& errMsg) override {
        j["good"] = good;
        j["quantity"] = quantity;
        j["totalVolume"] = totalVolume;
        childNodesToJson(j);
    }
protected:
    ~cargo() {

    }
};

然后创建一个描述ship的类,代码如下:

class ship:public Node {
public:
    NodeArray *cargos = new NodeArray();
    MyNode *mynode = new MyNode();
    ship(){
        cargos->push_back(new cargo());
        cargos->push_back(new cargo());
        cargos->push_back(new cargo());
        cargos->setNodeName("cargos");//To be a child node, NodeArray should have a name 
        addNode(cargos);//addNode后Node会由父类析构函数析构
        addNode(mynode);//addNode后Node会由父类析构函数析构
        //否则要在本类析构函数析构

    }
    void toJson(json& j, std::string& errMsg) override {
        json jcargos;
        cargos->toJson(jcargos,errMsg);
        j["cargos"] = jcargos;
        childNodesToJson(j);
        j["class"] = "MK-I Jackshaw OE-PM";
    }
protected:
    ~ship() {
    
    }
};

最后写个测试程序测试一下

int test() {  
    json jship;
    std::string err;

    ship* myship = new ship();
    myship->toJson(jship,err);
    myship = myship->release<ship>();

    json js;
    js["ship"] = jship;
   
    std::cout << js.dump(4);
    return 0;
}

输出为

{
    "ship": {
        "cargos": [
            {
                "good": "FUEL",
                "quantity": 17,
                "totalVolume": 17
            },
            {
                "good": "FUEL",
                "quantity": 17,
                "totalVolume": 17
            },
            {
                "good": "FUEL",
                "quantity": 17,
                "totalVolume": 17
            }
        ],
        "class": "MK-I Jackshaw OE-PM"
    }
}

写在最后:

做这个封装的目的在于,对于 json 的每个节点都可能动态变化的情况下,可以灵活的增加一个或大或小,深度或深或浅的预定义节点(或根据预定义节点组合的新节点),而不用修改代码。非常适合编辑 json。

而结构简单的小json,还是使用 JSON for Modern C++ 直接定义更方便。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值