携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
JSON 的定义
JSON: JavaScript Object Notation(JavaScript 对象表示法)
JSON 是存储和交换文本信息的语法,类似 XML。
JSON 比 XML 更小、更快,更易解析。
JSON 易于人阅读和编写。
有哪些数据?
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在中括号中)
- 对象(在大括号中)
- null
递归解析
思路:
json 可以分为这么两种值, 一种是可以包含其他值的,一种是不可能包含其他值的。
对于可以包含其他值的, 即: {key: value, ...} 和 [value, value, ..., value] 不可用包含其他值的,有 数字,字符串,逻辑值,null
对于这种包含其他值的value可以是任意一种类型。
所以就形成了一个递归包含的形式 所以我们可以递归的去解析他们
存储
| 存储 | Json_type |
|---|---|
| bool boolean_; | Boolean |
| double number_; | Number |
| std::string string_; | String |
| std::vector<Json> list_; | Array |
| std::map<std::string, Json> dict_; | Object |
| _ | NULL |
class Json {
public:
enum Json_type {
J_NULL_TYPE,
J_BOOL_TYPE,
J_NUMBER_TYPE,
J_STRING_TYPE,
J_LIST_TYPE,
J_DICT_TYPE
};
private:
Json_type type_; /* json type*/
bool boolean_; /*bool */
double number_; /int double float .../
std::string string_; /*string */
std::vector list_; /array/
std::map<std::string, Json> dict_; /object/
};
解析
首先匹配每种类型开始的特征
Json JsonParse::parse() {
char token = nextToken_();
switch (token) {
case 'n':
return parseNull();
case 'f':
case 't':
return parseBool();
case '-':
case '1':case '2':case '3':
case '4':case '5':case '6':
case '7':case '8':case '9':
return parseNumber();
case '\"':
return parseString();
case '[':
return parseList();
case '{':
return parseDict();
default:
throw std::logic_error("json grammar error");
}
return Json();
}
解析null
匹配4个字符 "null"
Json JsonParse::parseNull() {
if (!data_.compare(idx_, 4, "null")) {
idx_ += 4;
return Json();
} else {
throw std::logic_error("json grammar error");
}
}
解析boolean
匹配4个或5个字符 "true" | "false"
Json JsonParse::parseBool() {
bool flag;
if (data_[idx_] == 'f' && !data_.compare(idx_, 5, "false")) {
idx_ += 5;
flag = false;
return Json(Json::J_BOOL_TYPE, &flag);
} else if (data_[idx_] == 't' && !data_.compare(idx_, 4, "true")) {
idx_ += 4;
flag = true;
return Json(std::move(flag));
} else {
throw std::logic_error("json grammar error");
}
}
解析number
偷了一下懒,直接用strtod
Json JsonParse::parseNumber() {
double res = 0;
char* end;
res = strtod(&data_[idx_], &end);
if (&data_[idx_] == end) throw std::logic_error("json grammar error");
idx_ += end - &data_[idx_];
return Json(std::move(res));
}
解析string
在解析字符时,要注意转义字符的转换
std::string JsonParse::parseStr() {
auto start = ++idx_;
auto end = data_.find_first_of('"', start);
if (end == data_.npos) throw std::logic_error("json grammar error");
auto check = [&](int pos) -> bool {
int pre = pos;
while (pre && data_[pre] == '\\') pre--;
return (pos - pre) % 2 == 0;
};
while (1) {
if (check(end)) break;
end = data_.find_first_of('"', end + 1);
if (end == data_.npos) throw std::logic_error("json grammar error");
}
idx_ = end + 1;
return data_.substr(start, end - start);
}
解析array
注意解析时, 空白字符要跳过,此外就是一个状态的转移过程。问题不大
Json JsonParse::parseList() {
int flag = false;
idx_++;
std::vector<Json> list;
while (idx_ < data_.size()) {
char token = nextToken_();
if (token == ',') {
idx_++;
continue;
}
if (token == ']') {
idx_++;
flag = true;
break;
}
list.push_back(parse());
}
if (!flag) throw std::logic_error("json grammar error");
return Json(std::move(list));
}
解析object
解析每一个value时, key就是一个字符串, value 是任意一种的对象
Json JsonParse::parseDict() {
std::map<std::string, Json> dict;
int flag = false;
idx_++;
while (idx_ < data_.size()) {
char token = nextToken_();
if (token == ',') {
idx_++;
continue;
}
if (token == '}') {
idx_++;
flag = true;
break;
}
if (token != '\"') throw std::logic_error("json grammar error");
std::string key = parseStr();
token = nextToken_();
if (token != ':') throw std::logic_error("json grammar error");
idx_++;
dict.insert({key, parse()});
}
if (!flag) throw std::logic_error("json grammar error");
return Json(std::move(dict));
}
转成字符串
使用JSON时,另一个需求是把 类的对象 转换为一个 json 这个时候就需要把数据转为字符串了
转换状态转移
std::string Json::toString(int flag) {
std::string str;
switch (type_) {
case J_NULL_TYPE:
str.append(std::move(toNull_(flag)));
break;
case J_BOOL_TYPE:
str.append(std::move(toBool_(flag)));
break;
case J_NUMBER_TYPE:
str.append(std::move(toNumber_(flag)));
break;
case J_STRING_TYPE:
str.append(std::move(toString_(flag)));
break;
case J_LIST_TYPE:
str.append(std::move(toList_(flag)));
break;
case J_DICT_TYPE:
str.append(std::move(toDict_(flag)));
break;
}
return str;
}
转换null
std::string toNull_(int flag) { return "null"; }
转换boolean
std::string toBool_(int flag) {
if (boolean_) {
return "true";
} else {
return "false";
}
}
转换string
std::string toString_(int flag) {
std::string tmp;
tmp.push_back('\"');
tmp.append(std::move(string_));
tmp.push_back('\"');
return tmp;
}
转换number
std::string toNumber_(int flag) {
if (floor(number_) != number_) return std::to_string(number_);
return std::to_string(static_cast<int>(number_));
}
转换array
std::string toList_(int flag) {
std::string tmp;
tmp.push_back('[');
for (auto v : list_) {
tmp.append(std::move(v.toString(flag)));
tmp.push_back(',');
}
tmp.pop_back();
tmp.push_back(']');
return tmp;
}
转换object
std::string toDict_(int flag) {
std::string tmp;
tmp.push_back('{');
for (auto& v : dict_) {
tmp.push_back('\"');
tmp.append(std::move(v.first));
tmp.append("\":");
tmp.append(std::move(v.second.toString(flag)));
tmp.push_back(',');
}
tmp.pop_back();
tmp.push_back('}');
return tmp;
}
利用模板来自动匹配对应的dump
template <typename T>
lins::Json json_dump(T rsh) {return rsh.dump();}
// 特化一下 基础类型的dump
lins::Json json_dump(int rsh) { return lins::Json(static_cast<double>(rsh)); }
lins::Json json_dump(bool rsh) { return lins::Json(rsh); }
lins::Json json_dump(std::string rsh) { return lins::Json(std::move(rsh)); }
template <typename T>
lins::Json json_dump(std::vector<T> rhs) {
std::vector<lins::Json> json;
for (auto v : rhs) json.push_back(json_dump(v));
return lins::Json(std::move(json));
}
template <typename VALUE>
lins::Json json_dump(std::map<std::string, VALUE> rhs) {
std::map<std::string, lins::Json> json;
for (auto v : rhs) json.insert({v.first, json_dump(v.second)});
return lins::Json(std::move(json));
}
利用宏来定义dump
#define BEGIN_DUMP_JSON \
lins::Json dump() { \
std::map<std::string, lins::Json> res;
#define MAPPING(name, jsonName) res.insert({jsonName, lins::json_dump(name)});
#define END_DUMP_JSON \
return lins::Json(std::move(res)); \
}
使用
使用时,按需所取, 定义自己的load函数。
需要导出的对象就用DUMP宏来映射出去
示例定义
struct person {
std::string name;
std::string detail;
int age;
void load(const lins::Json& json) {
name = json["name"].STRING();
detail = json["detail"].STRING();
age = json["age"].NUMBER();
}
BEGIN_DUMP_JSON
MAPPING(name, "name");
MAPPING(detail, "detail");
MAPPING(age, "age");
END_DUMP_JSON
};
struct student {
int stuNo; // 学号
int classNo; // 班级
person info;
void load(const lins::Json& json) {
stuNo = json["stuNo"].NUMBER();
classNo = json["classNo"].NUMBER();
info.load(json["info"]);
}
BEGIN_DUMP_JSON
MAPPING(stuNo, "stuNo");
MAPPING(classNo, "classNo");
MAPPING(info, "info");
END_DUMP_JSON
};
struct theacher {
int studentNo;
person info;
void load(const lins::Json& json) {
studentNo = json["studentNo"].NUMBER();
info.load(json["info"]);
}
BEGIN_DUMP_JSON
MAPPING(studentNo, "studentNo");
MAPPING(info, "info");
END_DUMP_JSON
};
struct classroom {
std::vector<student> stu;
std::vector<theacher> tch;
std::map<string, int> mp;
void load(const lins::Json& json) {
for (auto& v : json["stu"].LIST()) {
auto t = student();
t.load(v);
stu.push_back(t);
}
for (auto& v : json["theacher"].LIST()) {
auto t = theacher();
t.load(v);
tch.push_back(t);
}
}
BEGIN_DUMP_JSON
MAPPING(stu, "stu");
MAPPING(tch, "tch");
MAPPING(mp, "mp");
END_DUMP_JSON
};
使用
classroom room = \
{
.stu{
student{1001, 5, {"张三", "一个学生", 15}},
student{1002, 5, {"李四", "一个学生", 16}},
student{1003, 5, {"王五", "学生一个", 19}},
student{1004, 5, {"坤坤", "唱跳rap篮球", 18}},
student{1005, 5, {"老六", "一个学生", 19}},
student{1006, 5, {"青霞", "一个学生", 19}},
},
.tch{
theacher{10086, {"李老师", "语文", 45}},
theacher{10087, {"张老师", "数学", 35}},
theacher{10081, {"结衣老师", "老师", 25}},
},
};
auto res = room.dump();
auto str = res.toString(true);
cout << lins::JsonParse(str).parse().toString() << endl;
完整代码
github.com/Amjieker/li… github.com/Amjieker/li…
测试代码