您现在的位置是: 网站首页 > 程序设计  > C++ 

RapidJson中的GetValueByPointer用法

2020年6月22日 08:00 1611人围观

简介没有简介

RapidJson 中的GetValueByPointer用法

{ 
    "person": { 
        "name": "Lucy", 
        "age": 22, 
        "address": { 
            "province": "广东", 
            "city": "深圳", 
            "street": "深南大道" 
        } 
    } 
} 

假如有这样一段json,想要解析出人名和省份,那么有一下方法

HasMember
rapidjson::Document doc; 
if (doc.Parse(str.c_str()).HasParseError()) 
{ 
    cout << "json error" << endl; 
} 

string name; 
string province; 
if (doc.HasMember("person") and doc["person"].IsObject()) 
{ 
    rapidjson::Value &person = doc["person"]; 
    if (person.HasMember("name") and person["name"].IsString()) 
    { 
        name = person["name"].GetString(); 
    } 
    if (person.HasMember("address") and person["address"].IsObject()) 
    { 
        rapidjson::Value &address = person["address"]; 
        if (address.HasMember("province") and address["province"].IsString()) 
        { 
            province = address["province"].GetString(); 
        } 
    } 
} 

cout << name << " live at " << province << endl; 

为什么要写的这么复杂?我们直接doc["person"]["name"]不行吗?很遗憾,的确不行。相信大家都对这个assert深受其害,所有在使用rapidjson解析的时候需要严格的做好类型判断,一不小心程序就assert了。如果每次获取前都需要先判断元素在不在,然后再判断元素的类型,那这样写起来太复杂了,这时候就出现了GetValueByPointer

GetValueByPointer

GetValueByPointer接受一个用/分割的字符串列表,表示从doc中查找的目录,比如GetValueByPointer(doc, "/person/address/province");表示以doc为根目录查找person>address>province字段,返回一个指针,通过指针就能找到该字段。

rapidjson::Document doc; 
if (doc.Parse(str.c_str()).HasParseError()) 
{ 
    cout << "json error" << endl; 
} 

string name; 
string province; 

rapidjson::Value *pName = GetValueByPointer(doc, "/person/name"); 
if (pName != NULL and pName->IsString()) 
{ 
    name = pName->GetString(); 
} 

rapidjson::Value *pProvince = GetValueByPointer(doc, "/person/address/province"); 
if (pProvince != NULL and pProvince->IsString()) 
{ 
    province = pProvince->GetString(); 
} 

cout << name << " live at " << province << endl; 

这样就不用每次在找一个节点的时候判断它的父节点是否存在,而且也不用判断父节点类型了。

FindMember

其实和GetValueByPointer的功能和FindMember是相似的,只不过是简化了,操作如下:

    rapidjson::Document doc; 
    if (doc.Parse(str.c_str()).HasParseError()) 
    { 
        cout << "json error" << endl; 
    } 

    string name; 
    string province; 

    rapidjson::Value::ConstMemberIterator iter1 = doc.FindMember("person"); 
    if (iter1 != doc.MemberEnd()) 
    { 
        const rapidjson::Value &v = iter1->value; 
        rapidjson::Value::ConstMemberIterator iter2 = v.FindMember("name"); 
        if (iter2 != v.MemberEnd() and iter2->value.IsString()) 
        { 
            name = iter2->value.GetString(); 

            rapidjson::Value::ConstMemberIterator iter3 = v.FindMember("address"); 
            if (iter3 != v.MemberEnd() and iter3->value.IsObject()) 
            { 
                rapidjson::Value::ConstMemberIterator iter4 = iter3->value.FindMember("province"); 
                if (iter4 != iter3->value.MemberEnd() && iter4->value.IsString()) 
                { 
                    province = iter4->value.GetString(); 
                } 
            } 
        } 
    } 

    cout << name << " live at " << province << endl; 

FindMember也能达到目的。通常来说,我们更愿意使用GetValueByPointer,它让代码更加简洁易读。

GetValueByPointerWithDefault

在一些语言中,当用[]访问map时,如果map有key,则返回value,否则插入空,再返回。rapidjson也提供了类似接口,而且还可以指定缺省值

Value&title=GetValueByPointerWithDefault(d,"/widget/window/title","untitled"); 

当解引用失败时,它会创建整个路径,并把预设值复制成新值,并返回该值。由于它总能返回一个值,此函数的返回类型为引用而不是指针。

CreateValueByPointer

这绝对是操作神器,可以快速创建json的层级节点,并且返回该节点,CreateValueByPointer(d,"/a").SetArray();,这样就能创建一个节点了。特别的,当层级token以负号"-"结尾时,他可以指向JSONArray最后元素的下一个元素,利用这种特性可以实现PushBack的效果。

rapidjson::Document d; 
CreateValueByPointer(d,"/a").SetArray(); 

for(int i = 1; i <=4 ;i ++) 
{ 
    //SetValueByPointer(d,"/a/-", i); 
    CreateValueByPointer(d,"/a/-").SetInt(i); 
} 

rapidjson::StringBuffer buffer; 
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); 
d.Accept(writer); 

cout << buffer.GetString() << endl; 

最后输出{"a":[1,2,3,4]},这样就达到了数组PushBack的效果了~