办公问答网

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 112|回复: 6

记录一下写c++ json库 受苦过程(一)

[复制链接]

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2023-4-10 20:32:13 | 显示全部楼层 |阅读模式
没有上一篇

这一篇:受苦过程(一)

下一篇:受苦过程(二)

玩具项目想要一个json类,干脆自己写个玩,于是记录一下可能的受苦过程,做到哪写到哪。
首先写个json库就要明确这个库究竟要干哪些事,这些事写道代码是什么样的,具体库用起来怎么用的?
那么根据Milo Yip:从零开始的 JSON 库教程中给的总结,可以概况成一下三种情况

  • 解析json字符串为json类
  • 获取json值
  • 把一个对象json化
那么具体用起来应该像下面这样
/*
        // get json
        Json json("file.json");
        // or
        Json json;
        json.read("file.json");
        // or
        ClassCanToJson cls;
        Json json(cls);
        // or
        Json json = cls.toJson();

        // get value
        auto val = json["key"];
        std::cout<<val<<std::endl;

        // to json file
        json.dump(output_file_path);
*/那么要做到这种效果需要Json类解析json文件,存储json的值,获取json值,同时需要将存储的值转化为json格式。

那Json类长得就该像这样
// Json.h
#pragma once
#include"Node.h"
class Json;
class CanJson
{
public:
        virtual Json toJson() = 0;
};
class Json
{
public:
        Json():jsons(std::make_shared<JsonArray>()){};
        ~Json() {};
        Json(const Json & js):jsons(js.jsons) {};
        Json(Json && js) :jsons(js.jsons) {};
        Json(const CanJson & cls) {};
        void addNode(const JsonNode & jn)
        {
                jsons->insert(jn);
        }
        void parse(const std::string_view & s) {};
        void read(const std::string & file_path) {};
        void dump() {};
        auto operator [](const std::string & s)
        {
                auto find_ret = jsons->find(JsonNode(s));
                if (find_ret == jsons->end())
                        throw std::runtime_error("wrong [] key is: " + s);
                return *find_ret;
        }
        std::string toString()const
        {
                std::string result;
                result += "{\n";
                for (auto & node : *jsons)
                {
                        result.push_back('\t');
                        result += node.toString();
                        result.push_back(',');
                        result.push_back('\n');
                }
                result.pop_back();
                result.pop_back();
                result += "\n}\n";
                return result;
        }
private:
        std::shared_ptr<JsonArray> jsons;
};
值可以用一个variant存,我这里用了一个比较蠢的抽象,在创建节点的时候传一个array_type,如果为真那就真的是json的array类型同时key为空,如果为假视为json类型key非空,于是就不支持key为""。用std::vistit来试存哪个类型。
// JsonValues.h
#pragma once
#include<string>
#include<set>
#include<variant>
#include<iostream>
#include"Node.h"
enum JSONTYPE:int { NULL_TYPE, BOOL_TYPE, NUMBER_TYPE,STRING_TYPE, ARRAY_TYPE, JSON_TYPE ,NO_TYPE = -1};
class JsonNode;
using String = std::string;
using Null = std::monostate;
using JsonArray = std::multiset <JsonNode>;
using Value = std::variant<Null, bool, double, String, JsonArray>;

struct JsonValue
{
        JsonValue() {};
        JsonValue(const Value &&val) :__value(std::move(val)) {}
        JsonValue(const Value &&val, bool array_type)
                :__value(std::move(val)), array_type(array_type) {}
        JsonValue(const JsonValue & v)
                :__value(v.__value), array_type(v.array_type), type(v.type){}
        JsonValue(JsonValue && v)
                :__value(std::move(v.__value)), array_type(v.array_type), type(v.type) {}
        Value __value = std::monostate{};
        bool array_type = false;
        JSONTYPE type = JSONTYPE::NO_TYPE;
        JSONTYPE getType()
        {
                if (type != JSONTYPE::NO_TYPE)
                        return type;
                std::visit(
                        [&](auto && arg)
                        {
                                using T = std::decay_t<decltype(arg)>;
                                if constexpr (std::is_same_v<T, double>)
                                {
                                        this->type = JSONTYPE::NUMBER_TYPE;
                                }
                                else if constexpr (std::is_same_v<T, String>)
                                {
                                        this->type = JSONTYPE::STRING_TYPE;
                                }
                                else if constexpr (std::is_same_v<T, Null>)
                                {
                                        this->type = JSONTYPE::NULL_TYPE;
                                }
                                else if constexpr (std::is_same_v<T, bool>)
                                {
                                        this->type = JSONTYPE::BOOL_TYPE;
                                }
                                else if constexpr (std::is_same_v<T, JsonArray>)
                                {
                                        this->type = this->array_type ? JSONTYPE::ARRAY_TYPE : JSONTYPE::JSON_TYPE;
                                }
                        }, __value);
                return type;
        }
        std::string toString()const
        {
                std::string result;
                std::visit(
                        [&](auto && arg)
                        {
                                using T = std::decay_t<decltype(arg)>;
                                if constexpr (std::is_same_v<T, double>)
                                {
                                        result += std::to_string(arg);
                                }
                                else if constexpr (std::is_same_v<T, String>)
                                {
                                        result.push_back('\"');
                                        result += arg;
                                        result.push_back('\"');
                                }
                                else if constexpr (std::is_same_v<T, Null>)
                                {
                                        result += "null";
                                }
                                else if constexpr (std::is_same_v<T, bool>)
                                {
                                        result += arg ? "true" : "false";
                                }
                                else if constexpr (std::is_same_v<T, JsonArray>)
                                {
                                        if (this->array_type)
                                        {
                                                result += '[';
                                                for (auto && v : arg)
                                                {
                                                        result += v.toString() + ",";
                                                }
                                                result.pop_back();
                                                result += ']';
                                        }
                                        else
                                        {
                                                result += '{';
                                                for (auto && v : arg)
                                                {
                                                        result += v.toString() + ",";
                                                }
                                                result.pop_back();
                                                result += '}';
                                        }
                                }
                        }, __value);
                return result;
        }
};
Json节点定义
#pragma once
#include"JsonValues.h"
inline std::ostream & operator <<(std::ostream & o, const JsonNode & jn);
class JsonNode
{
public:
        friend inline std::ostream & operator <<(std::ostream & o, const JsonNode & jn);
        explicit JsonNode(const std::string & key)
                :__key(std::move(key)) {}
        explicit JsonNode(const std::string & key, const JsonValue&val)
                :__key(std::move(key)), __value(std::move(val)) {}
        JsonNode(JsonNode && node)
                :__key(std::move(node.__key)), __value(std::move(node.__value)){}
        JsonNode(const JsonNode & node)
                :__key(node.__key),__value(node.__value){}
        std::string toString()const
        {
                return __key.size() == 0 ? __value.toString() : "\"" + __key + "\"" + " : " + __value.toString();
        }
        bool operator<(const JsonNode & b)const
        {
                return this->__key < b.__key;
        }
        auto operator [](const std::string & s)
        {
                JSONTYPE value_type = __value.getType();
                if (!(value_type == JSONTYPE::JSON_TYPE))
                        throw std::runtime_error("wrong [], value type is: " + value_type);
                // value must be a set
                auto & json_nodes = std::get<JsonArray>(__value.__value);
                auto find_ret = json_nodes.find(JsonNode(s));
                if (find_ret == json_nodes.end())
                        throw std::runtime_error("worng [] key is: " + s);
                return *find_ret;// a jsonvalue not a node
        }
        std::string & getKey()
        {
                return __key;
        }

private:
        std::string __key;
        JsonValue __value;
};

inline std::ostream & operator <<(std::ostream & o, const JsonNode & jn)
{
        o << jn.__value.toString();
        return o;
}
测试一下
#include"Json.h"
int main()
{
        /*
                // a node
                "a":[
                        "a_sub_num":1.0,
                        "a_sub_bool":false,
                        "a_sub_null":null,
                        "a_sub_string":"a string",
                        "a_sub_array":["array_string",true,{"a_sub_sub_num":100}]
                        ]
        */
        JsonNode node("a", JsonValue{
                JsonArray{
                        JsonNode{"a_sub_num",JsonValue{1.0}} ,
                        JsonNode{"a_sub_bool",JsonValue{false}},
                        JsonNode{"a_sub_null",JsonValue{std::monostate{}}},
                        JsonNode{"a_sub_string",JsonValue{String("a string")}},
                        JsonNode{"a_sub_array", JsonValue{
                                JsonArray{
                                        JsonNode{"",JsonValue{1.0}} ,
                                        JsonNode{"",JsonValue{true}} ,
                                        JsonNode{"",JsonValue{JsonArray{JsonNode{"array_string",JsonValue{100.0}}},false}}
                                },true
                        }}
                },false });
        // json node: {"key",value}
        // json type: JsonNode{"",JsonValue{JsonArray{node},false}}
        // json array type: JsonArray{nodes}
        std::cout << node.toString()<<std::endl;
        /*
                // a json
                {
                        "title": "Design Patterns",
                        "subtitle": "Elements of Reusable Object-Oriented Software",
                        "author": [
                            "Erich Gamma",
                            "Richard Helm",
                            "Ralph Johnson",
                            "John Vlissides"
                        ],
                        "year": 2009,
                        "weight": 1.8,
                        "hardcover": true,
                        "publisher": {
                            "Company": "Pearson Education",
                            "Country": "India",
                        },
                        "website": null
                }
        */
        JsonNode title("title", JsonValue{ String("Design Patterns") });
        JsonNode subtitle("subtitle", JsonValue{ String("Elements of Reusable Object-Oriented Software") });
        JsonNode author("author", JsonValue{
                        JsonArray{
                                JsonNode{"",JsonValue{String("Erich Gamma")}},
                                JsonNode{"",JsonValue{String("Richard Helm")}},
                                JsonNode{"",JsonValue{String("Ralph Johnson")}},
                                JsonNode{"",JsonValue{String("John Vlissides")}}
                        },
                        true
                }
        );
        JsonNode year("year", JsonValue{ 2009.0 });
        JsonNode weight("weight", JsonValue{ 1.8 });
        JsonNode hardcover("hardcover", JsonValue{ true });
        JsonNode publisher("publisher", JsonValue{
                        JsonArray{
                                JsonNode{"Company",JsonValue{String("Pearson Education")}},
                                JsonNode{"Country",JsonValue{String("India")}},
                        },
                        false
                }
        );
        JsonNode website("website", JsonValue{ std::monostate{} });
        Json js;
        js.addNode(title);
        js.addNode(subtitle);
        js.addNode(author);
        js.addNode(year);
        js.addNode(weight);
        js.addNode(hardcover);
        js.addNode(publisher);
        js.addNode(website);
        std::cout << js.toString() << std::endl;
        try
        {
                auto && company_ret = js["publisher"]["Company"];
                std::cout << company_ret << std::endl;

                auto && year_ret = js["year"];
                std::cout << year_ret << std::endl;

                auto && author_ret = js["author"];
                std::cout << author_ret << std::endl;

                auto && error_ret = js["abc"];
        }
        catch (const std::exception& e)
        {
                std::cerr << e.what();
                exit(1);
        }
        // auto error_ret = sub_ret["a"];
        return 0;
}
/*
output:
"a" : {"a_sub_array" : [1.000000,true,{"array_string" : 100.000000}],"a_sub_bool" : false,"a_sub_null" : null,"a_sub_num" : 1.000000,"a_sub_string" : "a string"}
{
        "author" : ["Erich Gamma","Richard Helm","Ralph Johnson","John Vlissides"],
        "hardcover" : true,
        "publisher" : {"Company" : "Pearson Education","Country" : "India"},
        "subtitle" : "Elements of Reusable Object-Oriented Software",
        "title" : "Design Patterns",
        "website" : null,
        "weight" : 1.800000,
        "year" : 2009.000000
}

"Pearson Education"
2009.000000
["Erich Gamma","Richard Helm","Ralph Johnson","John Vlissides"]
wrong [] key is: abc
*/
可以预计的是解析的时候大概率受苦,可能整个类都要推倒重来。
<hr/>更一下开局思路来源:铁甲万能狗:第30篇 C++的variant簡化樹形數據結構的構建
回复

使用道具 举报

2

主题

10

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2023-4-10 20:32:49 | 显示全部楼层
感觉用variant不一定行。用any我试过可以,我写过一个版本玩一玩。  variant要求定义出它可能保存的所有类型。但是json可以保存它自己(的array跟object)不知道这里会不会存在递归定义的问题。  当然其实nolhamman(这鬼单词比较难记,好像是类似这样个名字吧)的json对C++来说已经大多数场景可用了,除了追求极致性能(上rapidjson),其他应该直接用那个就行。  你想自己设计的话,也完全可以参考n家的那个api来设计。
回复

使用道具 举报

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2023-4-10 20:33:03 | 显示全部楼层
存自己肯定是存引用啊
回复

使用道具 举报

0

主题

7

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2023-4-10 20:33:24 | 显示全部楼层
variany是在模板参数里边定义的,你怎么在模板参数中你定义还没有初始化出来的模板类型?
回复

使用道具 举报

3

主题

10

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2023-4-10 20:34:20 | 显示全部楼层
class 包一层,模板参数填包装类型而不是 variant
回复

使用道具 举报

1

主题

6

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2023-4-10 20:35:03 | 显示全部楼层
二叉树的数据结构都已经能用variant写了json为什么不能https://www.bilibili.com/video/BV1ha411k7pa?t=2183.1&p=76
回复

使用道具 举报

1

主题

6

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2023-4-10 20:35:45 | 显示全部楼层
好像感觉大概在MSVC上没问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|办公问答网

GMT+8, 2025-7-5 15:10 , Processed in 0.092550 second(s), 22 queries .

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc. Templated By 【未来科技 www.veikei.com】设计

快速回复 返回顶部 返回列表