问题
I am parsing a JSON
structure which is similar as follows
{
"item1" : "value1"
"item2" : "value2"
// ...
"itemn" : {
"outernestedItem1" : {
"innerNestedItem1" : "valuen1"
"innerNestedItem2" : "valuen2"
}
// ....
"outernestedItemn" : {
"innerNestedItem1" : "valuen1"
"innerNestedItem2" : "valuen2"
}
}
}
The number of outer nested items is not fixed, so I was iterating using iterator from rapidjson, inner-nested objects variables are fixed, so I can get access to them using [].
const rapidjson::Value& itemn = document["itemn"];
for (rapidjson::Value::ConstMemberIterator itr = itemn.MemberBegin();
itr != itemn.MemberEnd(); ++itr)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer( sb );
itr->value.Accept(writer);
std::cout << sb["innerNestedItem1"].GetString();
std::cout << sb["innerNestedItem2"].GetString();
}
but [] is not allowed with sb(string buffer), any idea how can I do this?
Edit1: I did it in very inefficient way, but just sharing the solution, so it might help someone to come up with efficient solution.
const rapidjson::Value& itemn = document["itemn"];
for (rapidjson::Value::ConstMemberIterator itr = itemn.MemberBegin();
itr != itemn.MemberEnd(); ++itr)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer( sb );
itr->value.Accept(writer);
//changed from here onwards
rapidjson::Document for_outer_nested_item;
std::string temp = sb.GetString();
char buffer2[100000];
strcpy_s(buffer2, temp.c_str());
for_outer_nested_item.ParseInsitu(buffer2);
std::cout << executive_command["innerNestedItem1"].GetString() << std::endl;
std::cout << executive_command["innerNestedItem2"].GetString() << std::endl;
}
回答1:
First, let me provide credit to MiloYip at this link
Second-- here's what I did for my project:
rapidjson::Document document;
// document holds a json document retrieved from a http GET request
// I did not include all of that in this example. I am only showing
// the part of iterating through a nested object and retrieving members.
std::vector<std::string> symbols;
// holds the values I retrieve from the json document
if (document.Parse<0>( symbol.c_str() ).HasParseError() )
Log() << "ERROR: encountered a JSON parsing error" << std::endl;
else {
// Get the nested object that contains the elements I want.
// In my case, the nested object in my json document was results
// and the values I was after were identified as "t"
rapidjson::Value& results = document["results"];
assert(results.IsArray());
for (rapidjson::SizeType i = 0; i < results.Size(); i++) {
// Store the value of the element in a vector.
symbols.emplace_back(results[i]["t"].GetString());
}
I think this is a pretty clean/efficient approach.
回答2:
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!
回答3:
void parseRecursive(std::string scope
, rapidjson::Value::ConstMemberIterator object
, std::unordered_map<std::string, std::string>& values)
{
if (scope.empty())
{
scope = object->name.GetString();
}
else
{
scope = scope + "::" + object->name.GetString();
}
auto inElement = scope + "::";
if (object->value.IsObject())
{
for (auto it = object->value.MemberBegin(); it != object->value.MemberEnd(); ++it)
{
parseRecursive(scope, it, values);
}
}
else if (object->value.IsDouble())
{
values.emplace(inElement, std::to_string(object->value.GetDouble()));
}
else if (object->value.IsInt())
{
values.emplace(inElement, std::to_string(object->value.GetInt()));
}
else
{
LOGW("Unsuported: " << inElement << object->name.GetString());
}
}
And start with document: rapidjson::Document document;
for (auto it = document.MemberBegin(); it != document.MemberEnd(); ++it)
{
parseRecursive("", it, _values);
}
回答4:
a.raya203's post https://stackoverflow.com/a/43120359/6155053 didn't work for me out of the box (doesn't handle all types the current rapidjson version implements, and thus ran into errors when e.g. outputting gltfs containing doubles, etc.) but it brought me on the right track for understanding how rapidjson works for parsing a document, so I thought I'd leave my (updated) code here, maybe it helps out someone else…
#include <iostream>
#include <string>
class JsonNodePrinter final
{
public:
static void PrintNode(const rapidjson::Value &node, size_t indent = 0, unsigned int level = 0, const std::string& nodeName = "")
{
std::cout << GetIndentString(indent, level);
if (!nodeName.empty())
std::cout << nodeName << ": ";
if (node.IsBool())
std::cout << node.GetBool();
else if (node.IsInt())
std::cout << node.GetInt();
else if (node.IsUint())
std::cout << node.GetUint();
else if (node.IsInt64())
std::cout << node.GetInt64();
else if (node.IsUint64())
std::cout << node.GetUint64();
else if (node.IsDouble())
std::cout << node.GetDouble();
else if (node.IsString())
std::cout << node.GetString();
else if (node.IsArray())
{
if (!nodeName.empty()) std::cout << "\n" << GetIndentString(indent, level);
PrintArray(node, indent, level);
}
else if (node.IsObject())
{
if (!nodeName.empty()) std::cout << "\n" << GetIndentString(indent, level);
PrintObject(node, indent, level);
}
std::cout << "\n";
}
static void PrintObject(const rapidjson::Value &node, size_t indent = 0, unsigned int level = 0)
{
std::cout << "{\n";
for (rapidjson::Value::ConstMemberIterator childNode = node.MemberBegin(); childNode != node.MemberEnd(); ++childNode)
{
PrintNode(childNode->value, indent, level + 1, childNode->name.GetString());
}
std::cout << GetIndentString(indent, level) << "}";
}
static void PrintArray(const rapidjson::Value& node, size_t indent = 0, unsigned int level = 0)
{
std::cout << "[\n";
for (rapidjson::SizeType i = 0; i < node.Size(); ++i)
{
PrintNode(node[i], indent, level + 1);
}
std::cout << GetIndentString(indent, level) << "]";
}
static std::string GetIndentString(size_t indent = 0, unsigned int level = 0)
{
return std::move(std::string(level * indent, ' '));
}
};
Use it like
#include "3rdParty/rapidjson/document.h"
rapidjson::Document document;
{
document.Parse(FileHelper::ReadString(filePath)->c_str());
}
if (!document.HasParseError())
{
JsonNodePrinter::PrintNode(document, 4);
}
回答5:
I was wondering the same very recently, here is what i got :
#include "rapidjson\filereadstream.h"
#include "rapidjson\document.h"
#include "rapidjson\istreamwrapper.h"
#include <fstream>
#include <iostream>
using namespace rapidjson;
// Documentation : using file stream instead of C FILE pointers
// http://rapidjson.org/md_doc_stream.html#FileStreams
ifstream file_stream(filepath);
IStreamWrapper isw(file_stream);
Document doc;
doc.ParseStream(isw);
file_stream.close();
if(doc.HasMember(CONF_NODE)){
Value *config_node = &(doc[CONF_NODE]);
// Now I can use it like so:
std::cout << (*config_node)["My Other Json node"].GetString() << std::endl;
}
I used this trick several times to avoid using unterminably long accessing request like
doc["Node1"]["Node2"]["Node3"]...["NodeX"].GetType()and instead rely on pointers that could be used to virtually "split" the doc chain:
doc["Node1"]["Node2"]["Node3"]...["NodeX"].GetType() | | pointer1 | pointer2 (*pointer_on_Node_N)["Node N+1"] = doc["Node1"][...]["NodeN"]["Node N+1]
That was particularly handy when I needed to iterate over my file (when facing arrays).
来源:https://stackoverflow.com/questions/30896857/iterate-and-retrieve-nested-object-in-json-using-rapidjson