Retrieving a nested object inside a JSON string using rapidjson

坚强是说给别人听的谎言 提交于 2019-12-03 17:10:56

问题


I need to retrieve a nested object inside a JSON string and I'm trying to do it using rapidjson. All I've found is how to retrieve arrays and basic types, but not sub-objects. I have created the following toy example which gives an error:

rapidjson::Document document;
std::string test =  " { \"a\": { \"z\" : 21 } } ";
std::cout << test << std::endl;
if ( document.Parse<0>( test.c_str() ).HasParseError() ) {
    std::cout << "Parsing error" << std::endl;
} else {
    if ( document[ "a" ].IsObject() ) {
        std::cout << "OK" << std::endl;
        std::cout << document[ "a" ].GetString() << std::endl;
    }
}

This is the output when executed:

{ "a": { "z" : 21 } } 
OK
JSONTest: ../rapidjson/document.h:441: const typename Encoding::Ch* rapidjson::GenericValue<Encoding, Allocator>::GetString() const [with Encoding = rapidjson::UTF8<char>, Allocator = rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>]: Assertion `IsString()' failed. Aborted

How do I retrieve the inner object to continue my parsing? Thanks.

Edit: What I need is to obtain the string representation of the inner object so I can call another function that is going to parse it.

Edit 2: code that allows to retrieve the inner object as a string:

rapidjson::Document document;
std::string test =  "{\"a\":{\"z\":21}} ";
if ( document.Parse<0>( test.c_str() ).HasParseError() ) {
    std::cout << "Error parsing" << std::endl;
} else {
    if ( document[ "a" ].IsObject() ) {
        rapidjson::StringBuffer sb;
        rapidjson::Writer<rapidjson::StringBuffer> writer( sb );
        document[ "a" ].Accept( writer );
        std::cout << sb.GetString() << std::endl;
    }
}

回答1:


You need to iterate through object's members manually, as GetString() only works on string members, while document["a"] is an Object. You need to iterate through that object's members using MemberIterator variable. I had no practice in C* for more than 15 years, so I can only give a general idea of how it should work:

for (MemberIterator m = document["a"].MemberBegin(); m != document["a"].MemberEnd(); ++m) {
    std::cout << m.name << " " << (m.IsNumber()?m.GetNumber():m.GetString()) << endl;
}

Also, you might want to look at Accept() method, it seems to return a JSON string of an object you give it.




回答2:


If element is an object you can just access subproperties with []:

for (SizeType i = 0; i < layers.Size(); i++){   
  cout << layers[i]["name"].GetString() << endl;
}



回答3:


There is another great approach realized in rapidjson - JSON Pointers. They have experimental status and according to the documentation shall be included in v.1.1. Anyway this approach looks like XPATH for XML so to get nested value we can use syntax like

Value* tmpValue = GetValueByPointer(doc, "/result/job/blob");

I tried this functionality and in my opinion this is better than iterators.




回答4:


You can use a pointer to get the subobject:

Value& a = *GetValueByPointer(document, "/a");
int z = a["z"].GetInt();



回答5:


Here is one example code to get the nested object as rapidjson::Document object.

Document get_nested(Document &d, std::string key){
rapidjson::StringBuffer buffer;
const char *key_ctr = key.c_str();

assert(d[key_ctr].IsObject());

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

rapidjson::Document result;
rapidjson::StringStream s(buffer.GetString());
result.ParseStream(s);

return result;
}



回答6:


you can also use the pointer of Document:

Document *document= new Document();
document->parse( test.c_str());

and putting in the Value pointer and use it

Value *val= document;
val = &(*val)["a"];
val = &(*val)["z"];
cout << val->GetString();



回答7:


This is something I recently worked on:

void enter(const Value &obj, size_t indent = 0) { //print JSON tree

if (obj.IsObject()) { //check if object
    for (Value::ConstMemberIterator itr = obj.MemberBegin(); itr != obj.MemberEnd(); ++itr) {   //iterate through object   
        const Value& objName = obj[itr->name.GetString()]; //make object value

        for (size_t i = 0; i != indent; ++i) //indent
            cout << " ";

        cout << itr->name.GetString() << ": "; //key name

        if (itr->value.IsNumber()) //if integer
            std::cout << itr->value.GetInt() ;

        else if (itr->value.IsString()) //if string
            std::cout << itr->value.GetString();


        else if (itr->value.IsBool()) //if bool
            std::cout << itr->value.GetBool();

        else if (itr->value.IsArray()){ //if array

            for (SizeType i = 0; i < itr->value.Size(); i++) {
                if (itr->value[i].IsNumber()) //if array value integer
                    std::cout << itr->value[i].GetInt() ;

                else if (itr->value[i].IsString()) //if array value string
                    std::cout << itr->value[i].GetString() ;

                else if (itr->value[i].IsBool()) //if array value bool
                    std::cout << itr->value[i].GetBool() ;

                else if (itr->value[i].IsObject()){ //if array value object
                    cout << "\n  ";
                    const Value& m = itr->value[i]; 
                    for (auto& v : m.GetObject()) { //iterate through array object
                        if (m[v.name.GetString()].IsString()) //if array object value is string
                            cout << v.name.GetString() << ": " <<   m[v.name.GetString()].GetString();
                        else //if array object value is integer
                            cout << v.name.GetString() << ": "  <<  m[v.name.GetString()].GetInt();

                       cout <<  "\t"; //indent
                    }
                }
                cout <<  "\t"; //indent
            }
        }

        cout << endl; 
        enter(objName, indent + 1); //if couldn't find in object, enter object and repeat process recursively 
    }     
}
}

This can handle any type of JSON tree. All you have to do is pass a Value as such:

Value v = document.GetObject();
Value& m= v;
enter(m);

And you're done!



来源:https://stackoverflow.com/questions/12742840/retrieving-a-nested-object-inside-a-json-string-using-rapidjson

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!