目录
一、Json数据格式
二、JsonCpp介绍
2.1 Json::Value
2.2 序列化接口
2.3 反序列化接口
三、JsonCpp的使用
3.1 头文件包含
3.2 序列化
3.3 反序列化
四、补充
Json 是⼀种数据交换格式,它采⽤完全独⽴于编程语⾔的⽂本格式来存储和表示数据。
一、Json数据格式
Json 的数据类型包括对象,数组,字符串,数字等。
• 对象:使⽤花括号 { } 括起来的表⽰⼀个对象
• 数组:使⽤中括号 [ ] 括起来的表⽰⼀个数组
• 字符串:使⽤常规双引号 " " 括起来的表示⼀个字符串
• 数字:包括整形和浮点型,直接使用
C语言 代码表示:
char *name = "张三";
int age = 18;
int score[3] = { 100, 110, 96 };
Json 表示:
{
"姓名" : "张三",
"年龄" : 18,
"成绩" : [100, 110, 96],
"喜欢的人" :{
"姓名" : "小丽",
"年龄" : "18",
"成绩" : [ 110, 112, 110]
}
}
注意,在Json中,每个对象的各个数据用 , 分割,最后一行数据不需要使用 ,
二、JsonCpp介绍
上述 Json 只是为我们提供了一种格式,具体的实现还需要每种语言配有对应的框架,这里我们就开始认识一下 JsonCpp ,主要学习其中的三个类即可。
2.1 Json::Value
如果要将数据对象进行序列化,就需要先将对象存储到 Json::Value 对象中
如果要将数据传输后进行反序列化,即解析,会将数据对象放入到 Json::Value 对象中
class Json::Value
{
Value &operator=(const Value &other); //Value重载了[]和=,因此所有的赋值和获取数据都可以通过 =
Value& operator[](const std::string& key); //简单的⽅式完成 val["name"] = "xx";
Value& operator[](const char* key);
Value removeMember(const char* key); //移除元素
const Value& operator[](ArrayIndex index) const; //val["score"][0]
Value& append(const Value& value); //添加数组元素val["score"].append(88);
ArrayIndex size() const; //获取数组元素个数 val["score"].size();
std::string asString() const; //转string string name = val["name"].asString();
const char* asCString() const; //转char* char *name = val["name"].asCString();
Int asInt() const; //转int int age = val["age"].asInt();
float asFloat() const; //转float float weight = val["weight"].asFloat();
bool asBool() const; //转 bool bool ok = val["ok"].asBool();
};
下面简单认识一下其中的部分成员:
= 运算符重载:Value 重载了[]和=,因此所有的赋值和获取数据都可以通过 = 来赋值
[] 运算符重载:Value 重载了[]和=,因此所有的赋值和获取数据都可以通过 = 来赋值
例:val ["name"] = "张三"
[] 运算符重载:使 Value 中的数组成员可以直接使用下标访问
例: val["score"][0]
append 函数:直接追追加 Value 中的数组成员
例: val["score"].append(89) 如果不使用append,89会覆盖上一个score成员!
size 函数:返回 Value 中的数组成员的数量,方便遍历某成员
2.2 序列化接口
class JSON_API StreamWriter
{
virtual int write(Value const& root, std::ostream* sout) = 0;
}
class JSON_API StreamWriterBuilder : public StreamWriter::Factory
{
virtual StreamWriter* newStreamWriter() const;
}
重点是 StreamWriter::write() 作为序列化函数,
其中,Json::StreamWriterBuilder 是继承自 Json::StreamWriter 的工厂类,用于生产 Json::StreamWriter 对象
write 写入成功时,会返回一个0,不成功则会返回非0
第一个参数指的是要进行序列化的 Json::Value 对象
第二个参数指的是要写入的输出流
2.3 反序列化接口
class JSON_API CharReader
{
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, std::string* errs) = 0;
}
class JSON_API CharReaderBuilder : public CharReader::Factory
{
virtual CharReader* newCharReader() const;
}
重点是 Json::CharReader::parse() 作为反序列化函数,
其中,Json::CharReaderBuilder 是继承自 Json::CharReader 的工厂类,用于生产 Json::CharReader 对象
parse 返回值是布尔类型,若 parse 成功返回 true,反之返回 true
第一个参数是要反序列化数据的起始地址
第二个参数是要反序列化数据的末尾地址
第三个参数是反序列化后数据要写入的 Json::Value 对象,要进行地址传参
第四个参数是报错信息,可以定义一个 std::string 对象进行地址传参
三、JsonCpp的使用
3.1 头文件包含
如果你的环境中已经安装了 Json 库,那么我们只需要 jsoncpp/json/json.h 文件,所以头文件如下
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
此外,如果使用 makefile ,还需要连接上 jsoncpp ,这里我将序列化和反序列化的 demo 分成了两个文件,如下:
all:Serialize unSerialize
Serialize:Serialize.cpp
g++ -o $@ $^ -std=c++11 -ljsoncpp
unSerialize:unSerialize.cpp
g++ -o $@ $^ -std=c++11 -ljsoncpp
.PHONY:clean
clean:
rm -rf unSerialize Serialize
3.2 序列化
可以先定义变量存储我们需要传入 Json::Value 的值,然后直接定义一个 Json::Value 的对象,并为对象赋值。
随后需要序列化时不能直接使用 Json::StreamWriter ,而是要先定义一个 Json::StreamWriterBuilder 对象,再创建一个 Json::StreamWriter 对象的指针接收由 Json::StreamWriterBuilder 对象调用其 newStreamWriter() 的返回值。
最后,可以使用 writer 函数,并指定输出流对象。
//数据序列化
void Serialize()
{
const char* name = "XiaoMing";
const char*sex = "Man";
int age = 18;
int score[3] = { 100, 110, 96 };
Json::Value student;
student["Name"] = name;
student["Sex"] = sex;
student["Age"] = age;
student["Score"].append(score[0]);
student["Score"].append(score[1]);
student["Score"].append(score[2]);
//定义工厂类对象
Json::StreamWriterBuilder swb;
Json::StreamWriter *sw(swb.newStreamWriter());
sw->write(student, &std::cout);
std::cout << std::endl;
delete sw;
}
int main()
{
Serialize();
return 0;
}
同时,还需要释放 Json::StreamWriter 对象的内存,以后会进行进一步优化。
为了避免内存泄露,可以使用智能指针管理 Json::StreamWriter 对象,同时,为了代码更加简洁,可以对 Serialize() 进一步封装。将要序列化的信息作为传入参数,并使用输出型参数以字符串的方式输出序列化后的信息中,这里要使用到 stringstream 用作数据的缓冲区,用于 Json::Value 格式转换到 std::string 格式。
将序列化后的信息带回到字符串后,可以打印出字符串的信息来查看序列化后的信息。
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>
//数据序列化
bool Serialize(const Json::Value &val, std::string &body)
{
//中间缓冲区,将 Json::Value 格式转换为 std::string 格式
std::stringstream ss;
//定义工厂类对象
Json::StreamWriterBuilder swb;
std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
//如果 write 返回值不为零,则错误
int ret = sw->write(val, &ss);
if (ret != 0)
{
std::cout << "Serialize Error!";
return false;
}
body = ss.str();
return true;
}
int main()
{
const char* name = "XiaoMing";
const char*sex = "Man";
int age = 18;
int score[3] = { 100, 110, 96 };
Json::Value student;
std::string body;
student["Name"] = name;
student["Sex"] = sex;
student["Age"] = age;
student["Score"].append(score[0]);
student["Score"].append(score[1]);
student["Score"].append(score[2]);
Serialize(student, body);
//打印字符串信息
std::cout << body << std::endl;
return 0;
}
可以看到序列化的结果是一样的。
3.3 反序列化
反序列化我们直接使用封装,先定义一个工厂类 Json::CharReaderBuilder,然后使用工厂类生成一个 Json::CharReader 对象,调用该对象的类内方法 parse 进行反序列化。
//数据反序列化
bool unSerialize(std::string &body, Json::Value &val)
{
//定义工厂类
Json::CharReaderBuilder crb;
std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
//错误信息缓冲区
std::string errs;
bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &val, &errs);
if (ret == false)
{
std::cout << "unSerialize Error!";
return false;
}
return true;
}
在进行测试时,可以直接自定义一个序列化字符串,在 R"()" 的小括号中定义出序列化的信息,然后使用 Json::Value 进行接收并分别打印:
int main()
{
//序列化数据
std::string s = R"({"Name":"XiaoHong", "Sex":"Woman", "Age":18})";
//反序列化后的结果填入 Json::Value 中
Json::Value stu;
bool ret = unSerialize(s, stu);
if (ret == false)
{
return -1;
}
std::cout << "Name:" << stu["Name"].asString() << " Sex:" << stu["Sex"].asString() << " Age:" << stu["Age"].asString() << std::endl;
return 0;
}
同时,在输出时,可以使用字符串
四、补充
4.1 中文乱码
Json 也可以使用中文,如果你使用中文是乱码的话,如下:
那你可以在定义工厂类对象后添加以下信息:
// 设置 emitUTF8 使其输出原始的 UTF-8 编码而不是转义序列
swb["emitUTF8"] = true;
此时再重新编译,就可以得到不乱码的中文结果:
4.2 demo 源码
源码链接如下,有需要的可以参考 demo 源码:
从零实现JsonRpc框架: CPP项目——从零实现JsonRpc项目博客链接:https://blog.csdn.net/m0_75186846/category_12772639.html - Gitee.comhttps://gitee.com/bright-and-sparkling-at-night/JsonRpc/tree/master/demo/jsoncpp