1. JSON概述
JSON: JavaScript 对象表示法( JavaScript Object Notation) 。是一种轻量级的数据交换格式。 它基于ECMAScript的一个子集。许多编程语言都很容易找到JSON 解析器和 JSON 库。 JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。
2. 常用C/C++ JSON库
常用的C&C++的JSON库有cJSON、json-c、JsonCpp等,腾讯员工开源的一个RapidJSON以高性能著称。
3. windows 下使用VS编译jsoncpp
-
在windows下 将
jsoncpp-0.10.7.tar.gz
解压缩 -
进入到解压目录
jsoncpp-0.10.7
, 在进入到子目录makefiles\msvc2010
-
使用
vs
打开项目文件jsoncpp.sln
-
编译该项目, 生成库文件
-
生成的静态库存储目录
jsoncpp-0.10.7\makefiles\msvc2010\Debug
-
生成的静态库文件:
lib_json.lib
-
使用的准备工作:
- 将静态库
lib_json.lib
拿出备用 - 将库对应的头文件拿出, 头文件目录
jsoncpp-0.10.7\include\json
- 将静态库
注意点:
1.注意vs中的代码生成中的运行库一定要改成多线程调式(/MTd)
2.不使用库文件也行,使用源文件也行,可以在项目目录下直接执行python amalgamate.py
命令,会在dist
目录下生成两个头文件和一个源文件json-forwards.h
、json.h
和jsoncpp.cpp
。因为jsoncpp源码就四五千行,直接放在jsconcpp.cpp中和工程中其他代码一起编译也比较方便。
4.linux下的安装和部署
-
准备安装包
jsoncpp-0.10.7.tar.gz
scons-3.0.5.zip
-
解压缩
$ tar zxvf jsoncpp-0.10.7.tar.gz $ unzip scons-3.0.5.zip
-
安装scons -> 进入
scons-3.0.5.zip
的解压目录# 要使用管理员权限执行该命令 $ python setup.py install
-
安装 jsoncpp -> 进入
jsoncpp-0.10.7.tar.gz
的解压目录$ scons platform=linux-gcc # 将生成的动态库/静态库拷贝到系统的库目录中, 需要管理员权限 $ cp libs/linux-gcc-***/* /lib # 拷贝json的头文件到系统目录中, 需要管理员权限 $ cp include/json/ /usr/include/ -r # 创建动态库的链接文件, 需要管理员权限 ln -s /lib/libjson_linux-gcc-xxx_libmt.so /lib/libjson.so # 更新, 这样才能搜索到动态库 libjson.so。需要管理员权限 $ ldconfig # ubuntu需需要加sudo # 测试 $ ./bin/linux-gcc-***/test_lib_json Testing ValueTest/checkNormalizeFloatingPointStr: OK Testing ValueTest/memberCount: OK Testing ValueTest/objects: OK Testing ValueTest/arrays: OK .................. Testing BuilderTest/settings: OK Testing IteratorTest/distance: OK Testing IteratorTest/names: OK Testing IteratorTest/indexes: OK All 53 tests passed
-
编译 c++ 测试文件: json-test.cpp
$ g++ json-test.cpp -ljson -o json
5. jsoncpp类的使用
/*
三个类:
Value, Reader, FastWriter
Value类:
- 将json支持的数据类型进行了包装, 最终得到一种类型 -> Value类型
- 整形, 浮点型, 布尔类型, 字符串, json数组, json对象
- 判断Value对象中的原始数据类型
- 类型转换功能, 将Value对象转换成功原始数据
- 给对象/数组添加元素
Reader类: 反序列化, 将json字符串 解析成 Value 类型
FastWriter类: 将Value对象中的数据序列化为字符串
*/
// 如何使用Jsoncpp处理json数据
/*
需要在内存中将json数组组织起来 -> 序列化为字符串
1. 创建一个Value对象, 将数据添加到Value对象中
2. 将Value对象序列化 -> 字符串 , 使用 FastWriter类
3. 将字符串写入到文件中
*/
/*
磁盘有一个json文件 (有一个josn字符串) -> 将字符串数据反序列化到到内存中
1. 打开磁盘文件将json字符串读出
2. 将字符串数据解析到 Value 中, 使用 Reader类
3. 通过Value对象将Value中的数据依次读出
*/
/* 测试数据 */
[12, 12.34, true, "tom", ["jack", "ace", "robin"], {"sex":"man", "girlfriend":"lucy"}]
5.1 Value类
枚举类型 | 说明 | 翻译 |
---|---|---|
nullValue | ‘null’ value | 不表示任何数据,空值 |
intValue | signed integer value | 表示有符号整数 |
uintValue | unsigned integer value | 表示无符号整数 |
realValue | double value | 表示浮点数 |
stringValue | UTF-8 string value | 表示utf8格式的字符串 |
booleanValue | bool value | 表示布尔数 |
arrayValue | array value (ordered list) | 表示数组,即JSON串中的[] |
objectValue | object value (collection of name/value pairs) | 表示键值对,即JSON串中的{} |
-
封装和修改数据
// 直接使用=赋值就可以了 Value& operator=(Value other); // 因为Json::Value已经实现了各种数据类型的构造函数 Value(ValueType type = nullValue); Value(Int value); Value(UInt value); Value(Int64 value); Value(UInt64 value); Value(double value); Value(const char* value); Value(const char* begin, const char* end); Value(bool value); Value(const Value& other); Value(Value&& other);
-
检测保存的数据类型
// 检测保存的数据类型 bool isNull() const; bool isBool() const; bool isInt() const; bool isInt64() const; bool isUInt() const; bool isUInt64() const; bool isIntegral() const; bool isDouble() const; bool isNumeric() const; bool isString() const; bool isArray() const; bool isObject() const;
-
基础数据类型访问并转换
const char* asCString() const; JSONCPP_STRING asString() const; Int asInt() const; UInt asUInt() const; Int64 asInt64() const; UInt64 asUInt64() const; LargestInt asLargestInt() const; LargestUInt asLargestUInt() const; float asFloat() const; double asDouble() const; bool asBool() const;
-
对json数组的操作
ArrayIndex size() const; Value& operator[](ArrayIndex index); Value& operator[](int index); const Value& operator[](ArrayIndex index) const; const Value& operator[](int index) const; // 根据下标的index返回这个位置的value值 // 如果没找到这个index对应的value, 返回第二个参数defaultValue Value get(ArrayIndex index, const Value& defaultValue) const; const_iterator begin() const; const_iterator end() const; iterator begin(); iterator end();
-
对json对象的操作
// 联想stl中的map obj["hello"] = "world"; obj["hello"]; Value& operator[](const char* key); const Value& operator[](const char* key) const; Value& operator[](const JSONCPP_STRING& key); const Value& operator[](const JSONCPP_STRING& key) const; Value& operator[](const StaticString& key); // 通过key, 得到value值 Value get(const char* key, const Value& defaultValue) const; Value get(const char* begin, const char* end, const Value& defaultValue) const; Value get(const JSONCPP_STRING& key, const Value& defaultValue) const; Value get(const CppTL::ConstString& key, const Value& defaultValue) const; // 得到对象中所有的键值 typedef std::vector<std::string> Members; Members getMemberNames() const;
-
将Value对象数据序列化为string
// 序列化得到的字符串有样式 -> 带换行 -> 方便阅读 // 写配置文件的时候 std::string toStyledString() const;
-
数组追加元素
Value& append(const Value& value);
5.2 Reader 类
bool Json::Reader::parse(const std::string& document,
Value& root, bool collectComments = true);
参数:
- document: json格式字符串
- root: 传出参数, 存储了json字符串中解析出的数据
- collectComments: 保存json字符串中的注释信息 // /**/
// 通过begindoc和enddoc指针定位一个json字符串
// 这个字符串可以是完成的json字符串, 也可以是部分json字符串
bool Json::Reader::parse(const char* beginDoc, const char* endDoc,
Value& root, bool collectComments = true);
// write的文件流 -> ofstream -> wo
// read的文件流 -> ifstream -> ri
// 假设要解析的json数据在磁盘文件中
// is流对象指向一个磁盘文件, 读操作
bool Json::Reader::parse(std::istream& is, Value& root, bool collectComments = true);
5.3 FastWriter 类
// 将数据序列化 -> 单行
// 进行数据的网络传输
std::string Json::FastWriter::write(const Value& root);
6.这里实例介绍使用C++的json第三方库JsonCpp
主要介绍jsoncpp的新版本API操作,旧版的放在后面可以不看。
- 新版API
//写操作
void NewApiFastWriter(){
//生成Json
Value root, mail, subArray;
root["Name"] = "dxs4396";
root["age"] = 20;
mail["mail1"] = "xxx.@qq.com";
mail["mail2"] = "xxx.@163.com";
mail["mail3"] = "xxx.@outlook.com";
root["mail"] = mail;
subArray.append(100);
subArray.append(true);
root["Array"] = subArray;
#if 0
//无样式版本获得字符串
//方便进行数据的网络传输
FastWriter writer;
string json = writer.write(root);
#else
//有样式版本获得字符串
//写配置文件时候方便阅读
string json = root.toStyledString();
#endif
ofstream ofs("newtest.json");
if (ofs.is_open()) {
ofs << json;
ofs.close();
}
else {
cout << "ofstream file failed\n";
}
}
//字符串中读操作
void NewApiReader() {
//Reader类解析json字符串
Reader reader;
Value root;
const char* jsonstr = R"({"Name":"Joker000","age":21})";
if (!reader.parse(jsonstr, jsonstr + strlen(jsonstr), root)) {
cout << "json parse failed\n";
return;
}
string name = root["Name"].asString();
int age = root["age"].asInt();
cout << "name=" << name << " " << "age=" << age << endl;
}
//json文件中读取操作
void newApiFileReader() {
//Reader类解析磁盘json文件
Value root;
Reader reader;
ifstream ifs("newtest.json");
if (!reader.parse(ifs, root)) {
cout << "failed\n";
}
else {
cout << "success\n";
}
if (root.isArray()) {
cout << "这是一个数组" << endl;
//根据写操作中生成的json文件,不存在的省略即可。
}
else {
cout << "这是一个对象" << endl;
//按照json格式解析
//遍历对象
Value::Members keys = root.getMemberNames();
for (int i = 0; i < keys.size(); ++i) {
if (root[keys[i]].isArray()) {
Value subVal = root[keys[i]];
for (int j = 0; j < subVal.size(); ++j) {
if (subVal[j].isInt()) {
cout <<keys[i]<< i << ":" << subVal[j].asInt() << endl;
}
else if (subVal[j].isBool()) {
cout <<keys[i]<< i << ":" << subVal[j].asBool() << endl;
}
else {
//根据例子写的,不存在的类型省略了
}
}
}
else if (root[keys[i]].isString()) {
cout << keys.at(i) << ":" << root[keys[i]].asString() << endl;
}
else if (root[keys[i]].isInt()) {
cout << keys.at(i) << ":" << root[keys[i]].asInt() << endl;
}
else if (root[keys[i]].isObject()) {
Value::Members mem = root[keys[i]].getMemberNames();
for (int k = 0; k < mem.size(); ++k) {
if (root[keys[i]][mem[k]].isString()) {
cout <<keys[i] << ":" << root[keys[i]][mem[k]].asString() << endl;
}
else {
//根据例子写的,不存在的类型省略了
}
}
}
}
}
ifs.close();
}
- 旧版API(可忽略,用新版即可)
//流中读操作
int readFromStream() {
Value root;
ifstream ifs;
ifs.open("test.json");
CharReaderBuilder builder;
builder["collectComments"] = true;
string errs;
if (!parseFromStream(builder, ifs, &root, &errs)) {
cout << errs << endl;
return EXIT_FAILURE;
}
cout << root << endl;
return EXIT_SUCCESS;
}
//字符串中读操作
int ReadFromString() {
//这个R"()"这个时C++新特性,可以不用写转义字符。
const string rawJson = R"({"Age":20,"Name":"Joker"})";
const auto rawJsonLenghth = static_cast<int>(rawJson.length());
constexpr bool shouldUserOldWay = false;
string err;
Value root;
if (shouldUserOldWay) {
Reader reader;
reader.parse(rawJson, root);
}
else {
CharReaderBuilder builder;
const unique_ptr<CharReader> reader(builder.newCharReader());
if (!reader->parse(rawJson.c_str(), rawJson.c_str() + rawJsonLenghth, &root, &err)) {
cout << "error" << endl;
return EXIT_FAILURE;
}
}
const string name = root["Name"].asString();
const int age = root["Age"].asInt();
cout << "name:" << name<<"age:" <<age<< endl;
return EXIT_SUCCESS;
}
//写入流中操作
int StreamWrite() {
Value root;
StreamWriterBuilder builder;
const unique_ptr<StreamWriter> writer(builder.newStreamWriter());
root["Name"] = "Joker";
root["Age"] = 20;
writer->write(root, &cout);
return EXIT_SUCCESS;
}
//写如字符串中操作
int StringWrite() {
Value root;
Value data;
constexpr bool shouldUseOldWay = false;
root["name"] = "Joker";
data["age"] = 20;
root["data"] = data;
if (shouldUseOldWay) {
FastWriter writer;
const string json_file = writer.write(root);
cout << json_file << endl;
}
else {
StreamWriterBuilder builder;
const string json_file = writeString(builder, root);
cout << json_file << endl;
}
return EXIT_SUCCESS;
}