|
没有上一篇
这一篇:受苦过程(一)
下一篇:受苦过程(二)
玩具项目想要一个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&#34;Node.h&#34;
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(&#34;wrong [] key is: &#34; + s);
return *find_ret;
}
std::string toString()const
{
std::string result;
result += &#34;{\n&#34;;
for (auto & node : *jsons)
{
result.push_back(&#39;\t&#39;);
result += node.toString();
result.push_back(&#39;,&#39;);
result.push_back(&#39;\n&#39;);
}
result.pop_back();
result.pop_back();
result += &#34;\n}\n&#34;;
return result;
}
private:
std::shared_ptr<JsonArray> jsons;
};
值可以用一个variant存,我这里用了一个比较蠢的抽象,在创建节点的时候传一个array_type,如果为真那就真的是json的array类型同时key为空,如果为假视为json类型key非空,于是就不支持key为&#34;&#34;。用std::vistit来试存哪个类型。
// JsonValues.h
#pragma once
#include<string>
#include<set>
#include<variant>
#include<iostream>
#include&#34;Node.h&#34;
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(&#39;\&#34;&#39;);
result += arg;
result.push_back(&#39;\&#34;&#39;);
}
else if constexpr (std::is_same_v<T, Null>)
{
result += &#34;null&#34;;
}
else if constexpr (std::is_same_v<T, bool>)
{
result += arg ? &#34;true&#34; : &#34;false&#34;;
}
else if constexpr (std::is_same_v<T, JsonArray>)
{
if (this->array_type)
{
result += &#39;[&#39;;
for (auto && v : arg)
{
result += v.toString() + &#34;,&#34;;
}
result.pop_back();
result += &#39;]&#39;;
}
else
{
result += &#39;{&#39;;
for (auto && v : arg)
{
result += v.toString() + &#34;,&#34;;
}
result.pop_back();
result += &#39;}&#39;;
}
}
}, __value);
return result;
}
};
Json节点定义
#pragma once
#include&#34;JsonValues.h&#34;
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() : &#34;\&#34;&#34; + __key + &#34;\&#34;&#34; + &#34; : &#34; + __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(&#34;wrong [], value type is: &#34; + 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(&#34;worng [] key is: &#34; + 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&#34;Json.h&#34;
int main()
{
/*
// a node
&#34;a&#34;:[
&#34;a_sub_num&#34;:1.0,
&#34;a_sub_bool&#34;:false,
&#34;a_sub_null&#34;:null,
&#34;a_sub_string&#34;:&#34;a string&#34;,
&#34;a_sub_array&#34;:[&#34;array_string&#34;,true,{&#34;a_sub_sub_num&#34;:100}]
]
*/
JsonNode node(&#34;a&#34;, JsonValue{
JsonArray{
JsonNode{&#34;a_sub_num&#34;,JsonValue{1.0}} ,
JsonNode{&#34;a_sub_bool&#34;,JsonValue{false}},
JsonNode{&#34;a_sub_null&#34;,JsonValue{std::monostate{}}},
JsonNode{&#34;a_sub_string&#34;,JsonValue{String(&#34;a string&#34;)}},
JsonNode{&#34;a_sub_array&#34;, JsonValue{
JsonArray{
JsonNode{&#34;&#34;,JsonValue{1.0}} ,
JsonNode{&#34;&#34;,JsonValue{true}} ,
JsonNode{&#34;&#34;,JsonValue{JsonArray{JsonNode{&#34;array_string&#34;,JsonValue{100.0}}},false}}
},true
}}
},false });
// json node: {&#34;key&#34;,value}
// json type: JsonNode{&#34;&#34;,JsonValue{JsonArray{node},false}}
// json array type: JsonArray{nodes}
std::cout << node.toString()<<std::endl;
/*
// a json
{
&#34;title&#34;: &#34;Design Patterns&#34;,
&#34;subtitle&#34;: &#34;Elements of Reusable Object-Oriented Software&#34;,
&#34;author&#34;: [
&#34;Erich Gamma&#34;,
&#34;Richard Helm&#34;,
&#34;Ralph Johnson&#34;,
&#34;John Vlissides&#34;
],
&#34;year&#34;: 2009,
&#34;weight&#34;: 1.8,
&#34;hardcover&#34;: true,
&#34;publisher&#34;: {
&#34;Company&#34;: &#34;Pearson Education&#34;,
&#34;Country&#34;: &#34;India&#34;,
},
&#34;website&#34;: null
}
*/
JsonNode title(&#34;title&#34;, JsonValue{ String(&#34;Design Patterns&#34;) });
JsonNode subtitle(&#34;subtitle&#34;, JsonValue{ String(&#34;Elements of Reusable Object-Oriented Software&#34;) });
JsonNode author(&#34;author&#34;, JsonValue{
JsonArray{
JsonNode{&#34;&#34;,JsonValue{String(&#34;Erich Gamma&#34;)}},
JsonNode{&#34;&#34;,JsonValue{String(&#34;Richard Helm&#34;)}},
JsonNode{&#34;&#34;,JsonValue{String(&#34;Ralph Johnson&#34;)}},
JsonNode{&#34;&#34;,JsonValue{String(&#34;John Vlissides&#34;)}}
},
true
}
);
JsonNode year(&#34;year&#34;, JsonValue{ 2009.0 });
JsonNode weight(&#34;weight&#34;, JsonValue{ 1.8 });
JsonNode hardcover(&#34;hardcover&#34;, JsonValue{ true });
JsonNode publisher(&#34;publisher&#34;, JsonValue{
JsonArray{
JsonNode{&#34;Company&#34;,JsonValue{String(&#34;Pearson Education&#34;)}},
JsonNode{&#34;Country&#34;,JsonValue{String(&#34;India&#34;)}},
},
false
}
);
JsonNode website(&#34;website&#34;, 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[&#34;publisher&#34;][&#34;Company&#34;];
std::cout << company_ret << std::endl;
auto && year_ret = js[&#34;year&#34;];
std::cout << year_ret << std::endl;
auto && author_ret = js[&#34;author&#34;];
std::cout << author_ret << std::endl;
auto && error_ret = js[&#34;abc&#34;];
}
catch (const std::exception& e)
{
std::cerr << e.what();
exit(1);
}
// auto error_ret = sub_ret[&#34;a&#34;];
return 0;
}
/*
output:
&#34;a&#34; : {&#34;a_sub_array&#34; : [1.000000,true,{&#34;array_string&#34; : 100.000000}],&#34;a_sub_bool&#34; : false,&#34;a_sub_null&#34; : null,&#34;a_sub_num&#34; : 1.000000,&#34;a_sub_string&#34; : &#34;a string&#34;}
{
&#34;author&#34; : [&#34;Erich Gamma&#34;,&#34;Richard Helm&#34;,&#34;Ralph Johnson&#34;,&#34;John Vlissides&#34;],
&#34;hardcover&#34; : true,
&#34;publisher&#34; : {&#34;Company&#34; : &#34;Pearson Education&#34;,&#34;Country&#34; : &#34;India&#34;},
&#34;subtitle&#34; : &#34;Elements of Reusable Object-Oriented Software&#34;,
&#34;title&#34; : &#34;Design Patterns&#34;,
&#34;website&#34; : null,
&#34;weight&#34; : 1.800000,
&#34;year&#34; : 2009.000000
}
&#34;Pearson Education&#34;
2009.000000
[&#34;Erich Gamma&#34;,&#34;Richard Helm&#34;,&#34;Ralph Johnson&#34;,&#34;John Vlissides&#34;]
wrong [] key is: abc
*/
可以预计的是解析的时候大概率受苦,可能整个类都要推倒重来。
<hr/>更一下开局思路来源:铁甲万能狗:第30篇 C++的variant簡化樹形數據結構的構建 |
|