以前VC解析JSON用的是第三方库JSONCpp,但是JSONCpp对我现在要做的项目来说有缺陷,不支持Unicode的CString解析,要经过繁琐的转化,网上也缺少用的信息来解决这个问题。现在用RapidJson试着来解决这个问题。
简介
RapidJson是腾讯推出的轻量级的高效的 C++ JSON 解析/生成器。说他高效肯定有些人不服,那么有多高效呢?这里有他的作者对 28 个 C/C++ JSON 库的比较,即便是老牌的JsonCpp库在效率上依然跟他差了一个量级。更让我看重的是他下面这个优点
RapidJSON 对 Unicode 友好。它支持 UTF-8、UTF-16、UTF-32 (大端序/小端序),并内部支持这些编码的检测、校验及转码。例如,RapidJSON 可以在分析一个 UTF-8 文件至 DOM 时,把当中的 JSON 字符串转码至 UTF-16。它也支持代理对(surrogate pair)及 "\u0000"(空字符)。
工作
首先这是下载地址,将include文件夹里的RapidJSON文件夹复制到自己工程,这个库是只有头文件的,不用编译静态库。
RapidJSON是解析一个 JSON 至DOM(Document Object Model, DOM),然后就可以轻松查询及修改 DOM,每个 JSON 值都储存为 Value 类。在源码中,有声明Document和Value的代码,如下
namespace rapidjson {
template >
class GenericValue {
// ...
};
template >
class GenericDocument : public GenericValue {
// ...
};
typedef GenericValue > Value;
typedef GenericDocument > Document;
} // namespace rapidjson
封装自己的解析类,首先声明需要的头文件,命名空间等,需要用到Unicode的CString的话,需要像如下声明。
#include "rapidjson/document.h"
using namespace rapidjson;//命名空间
typedef GenericDocument > WDocument; //定义16位Unicode的Document
typedef GenericValue > WValue; // 定义16位Unicode的Value
做出声明后,就可以用UTF16的Unicode了,下面是主要的解析的过程,用起来比JSONCpp简单好多。
//CString content = _T("{\"data\":[{\"name\":\"哈利波特\", \"birth\":\"2010 / 2 / 3\"},{\"name\":\"漩涡鸣人\",\"birth\":\"2010 / 3 / 4\"}]}\");
int rtn = 0;
WDocument jsonDoc;//生成一个dom
jsonDoc.Parse(content);
//判断字符串是否有错误
if (jsonDoc.HasParseError()) {
TRACE(L"Json Parse error:%d", jsonDoc.GetParseError()); //打印错误编号
rtn = 0xffffffff;
}
if (0 == rtn)
{
//获取json串中的数据
if (jsonDoc.HasMember(_T("data"))) {
WValue v_data;
v_data = jsonDoc[_T("data")];
//判断Array
if (v_data.IsArray() && !v_data.Empty())
{
WValue v_tempData;
//取Array中的数据
for (SizeType i = 0; i < v_data.Size(); i++) {
v_tempData = v_data[i];
if (v_tempData.IsObject()) {
//要先判断是否有数据,否则会异常
if (v_tempData.HasMember(_T("name"))) {
//在此做出处理
}
else
{
rtn = 0xfffffffe;
}
if (v_tempData.HasMember(_T("birth"))) {
//在此做出处理
}
else
{
rtn = 0xfffffffe;
}
}
else
{
rtn = 0xfffffffd;
}
}
}
}
else {
rtn = 0xfffffffc;
}
}
我这里只是做个简单的demo,想直接解析CString的Unicode格式的人可以参考下。
补充JSON的去耦
聪明的小伙伴很快发现上面的写法有一个很大的问题,就是扩展性非常差,当JSON需要扩展内容时,需要不停得追加语句。这里我提一个我自己的解决思路,先拿之前的JSON例子来说明:
{"data":
[
{"name":"哈利波特", "birth":"2010 / 2 / 3", "info":
[
{
"ability":"magic",
"level":"10",
"somethingmore":{"sth1":1,"sth2":2}
},
{
"ability":"fly",
"level":"9",
"somethingmore":{"sth1":3,"sth2":4}
}
]
}
{"name":"漩涡鸣人"...}
]
}
这里的JSON已经比第一份做出了扩展,那么如何处理呢?首先建立A类
class CA
{
public:
CA();
virtual ~CA();
CStringArray leveloneStrArray;
CStringArray leveltwoStrArray;
CStringArray levelthreeStrArray;
CStringArray levelfourStrArray;
}
#include "A.h"
CA::CA()
{
leveloneStrArray.Add(_T("1")); //用来判断层级
leveloneStrArray.Add(_T("name"));
leveloneStrArray.Add(_T("birth"));
leveloneStrArray.Add(_T("info"));
leveltwoStrArray.Add(_T("2"));//用来判断层级
leveltwoStrArray.Add(_T("ability"));
leveltwoStrArray.Add(_T("level"));
leveltwoStrArray.Add(_T("somethingmore"));
levelthreeStrArray.Add(_T("3"));//用来判断层级
levelthreeStrArray.Add(_T("sth1"));
levelthreeStrArray.Add(_T("sth2"));
}
先将需要解析的字段名称和层级都分类好。然后在解析类里用递归函数解析整个JSON。
//将CString 转为WValue
WValue CStringToRapidjsonValue(CString in)
{
WValue out(in.GetString(), in.GetLength());
return out;
}
//入参分别为JSON的value 以及需要解析层的字符串数组
int A::JsonCode(WValue &wValue, CStringArray &strArr,)
{
int rtn = 0;
int paramCount = strArr.GetCount();
CString sLevel = strArr.GetAt(0);
int lLevel = _ttoi(sLevel);
for (int i = 1; i < paramCount; i++)
{
CString paramStr = strArr.GetAt(i);
WValue ¶mValue = CStringToRapidjsonValue(paramStr);
if (wValue.HasMember(paramValue) && !wValue[paramValue].IsNull())
{
if (wValue[paramValue].IsArray())
{
for (SizeType j = 0; j < wValue[paramValue].Size(); j++)
{
WValue &iteratorValue = wValue[paramValue][j];
if (lLevel == 1){
JsonCode(iteratorValue, leveltwoStrArray);
....//此处判断层级,来决定传入下一层的参数,递归调用
}
else {
}
}
}
else if (wValue[paramValue].IsString())
{
CString endStr = wValue[paramValue].GetString();
}
else if (wValue[paramValue].IsInt())
{
int endInt = wValue[paramValue].GetInt();
}
}
}
return rtn;
}
这样做以后,当需要追加JSON内容的时候,只需要对字符串数组CStringArray
进行修改。