既然看到了这篇文章你肯定知道Json是什么东西了,网上有很多解析Json的开源库,最近工作需要用到Json解析,研究发现jsoncpp使用很方便,网上也有很多使用方法介绍,目前为止能查到的基本都是比较老的API使用方法,编译器会提醒使用的某些API已经被弃用了,有的编译器还会直接报错,既然推荐用新的那就学一下新API的使用方法吧,记录一下。

JSONCPP是C++中的生成与解析JSON 字符串的一种实现。JSON是一种人比较容易理解,机器也比较容易解析的轻量级的数据交换格式。可以从Github上下载jsoncpp, https://github.com/open-source-parsers/jsoncpp

使用方式

使用静态库/动态库

官方推荐使用Ninja进行编译,项目说明里有介绍。当然也可以使用比较常用的cmake进行编译

mkdir -p build/debug
cd build/debug
cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../..
make

生成的静/动态库文件目录为build/debug/src/lib_json/,使用的时候把include头文件加到工程中,链接使用-ljsoncpp参数指定链接库即可。

使用源文件

相比使用静态库,我更倾向于直接使用源文件,感觉这样比较方便。
在项目目录中直接执行python amalgamate.py命令,会在dist目录下生成两个头文件和一个源文件json-forwards.h 、json.hjsoncpp.cpp。因为jsoncpp源码就四五千行,直接放在jsconcpp.cpp中和工程中其他代码一起编译也比较方便。

新API

这里随便写一个Json数据来演示,下面这个Json串包含了数组和内嵌Json串,一般常见的也就这样的吧。

{
  "Name": "Liming",
  "Age": 26,
  "Language": ["C++", "Java"],
  "E-mail": {
    "Netease": "lmshao@163.com",
    "Hotmail": "liming.shao@hotmail.com"
  }
}

生成Json字符串

#include <iostream>
#include "json/json.h"

std::string createJson()
{
    std::string jsonStr;
    Json::Value root, lang, mail;
    Json::StreamWriterBuilder writerBuilder;
    std::ostringstream os;

    root["Name"] = "Liming";
    root["Age"] = 26;

    lang[0] = "C++";
    lang[1] = "Java";
    root["Language"] = lang;

    mail["Netease"] = "lmshao@163.com";
    mail["Hotmail"] = "liming.shao@hotmail.com";
    root["E-mail"] = mail;

    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(root, &os);
    jsonStr = os.str();

    std::cout << "Json:\n" << jsonStr << std::endl;
    return jsonStr;
}

调用这个函数会输出

Json:
{
	"Age" : 26,
	"E-mail" : 
	{
		"Hotmail" : "liming.shao@hotmail.com",
		"Netease" : "lmshao@163.com"
	},
	"Language" : 
	[
		"C++",
		"Java"
	],
	"Name" : "Liming"
}

解析Json字符串

解析Json的前提是我们知道这个Json字符串里面包含了哪些数据,针对上面函数生成的Json串,我们使用下面这个函数来解析其中的数据。

bool parseJson(const std::string &info)
{
    if (info.empty())
        return false;

    bool res;
    JSONCPP_STRING errs;
    Json::Value root, lang, mail;
    Json::CharReaderBuilder readerBuilder;

    std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
    res = jsonReader->parse(info.c_str(), info.c_str()+info.length(), &root, &errs);
    if (!res || !errs.empty()) {
        std::cout << "parseJson err. " << errs << std::endl;
    }

    std::cout << "Name: " << root["Name"].asString() << std::endl;
    std::cout << "Age: " << root["Age"].asInt() << std::endl;

    lang = root["Language"];
    std::cout << "Language: ";
    for (int i = 0; i < lang.size(); ++i) {
        std::cout << lang[i] << " ";
    }
    std::cout << std::endl;

    mail = root["E-mail"];
    std::cout << "Netease: " << mail["Netease"].asString() << std::endl;
    std::cout << "Hotmail: " << mail["Hotmail"].asString() << std::endl;

    return true;
}

在main函数里面调用这个函数,以上一个函数的返回值作为输入参数会有如下输出:

Name: Liming
Age: 26
Language: "C++" "Java" 
Netease: lmshao@163.com
Hotmail: liming.shao@hotmail.com

旧API

网上一搜多数都是旧API,懒得整理测试了,大概是这面这个样子。嵌套方式和上面新API操作一样。

生成Json

	Json::Value root, ex;
	Json::FastWriter writer;

	root["Name"] = "Lucy";
	root["age"] = 20;
	ex[0] = "ABC";
	ex[1] = "DEF";
	root["exinfo"] = ex;

	string json = writer.write(root);

解析Json

	Json::Reader reader;
	Json::Value root;
	const char *jsonStr = "{\"Name\":\"Lucy\",\"Age\":20}";
	
	if (!reader.parse(jsonStr, jsonStr + strlen(jsonStr), root)) {
        std::cout << "json parse failed\n";
        return 1;
    }

    std::string name = root["Name"].asString();
    int age = root["Age"].asInt();
    

使用旧API编译器有诸如下面的提醒,有点编译器会报warning,有的编译器会报error,

warning: 'Reader' is deprecated: Use CharReader and CharReaderBuilder instead [-Wdeprecated-declarations]

warning: 'FastWriter' is deprecated: Use StreamWriterBuilder instead [-Wdeprecated-declarations]

坚持使用旧API可以在文件头部加入这段代码:

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(disable : 4996)
#endif

总结

可以把Json::Value理解为一个Json结构,这个类型可以嵌套,也可以当做数组一样使用,在需要的时候可以把这个结构转为相应的类型。
比如:
我们已经知道root["Name"]存放的是一个string,可以把使用.asString()转换为string类型;
知道root["Language"]这个Json::Value是一个数组,就可以像数组一样使用下标进行取值;
知道root["E-mail"]这个Json::Value里面是嵌套的一个json,就可以和root结构一样进行处理。
Json::Value其他的成员函数还有

// 转换类型
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;
// 检测类型
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;

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐