问题
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