C++ 使用 Rapidjson 迭代和检索 JSON 中的嵌套对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30896857/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iterate and retrieve nested object in JSON using rapidjson
提问by Ruturaj
I am parsing a JSON
structure which is similar as follows
我正在解析一个JSON
类似如下的结构
{
"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 [].
外部嵌套项的数量不固定,所以我使用rapidjson 中的迭代器进行迭代,内部嵌套对象变量是固定的,所以我可以使用[] 访问它们。
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?
但是 [] 不允许与 sb(string buffer) 一起使用,知道我该怎么做吗?
Edit1: I did it in very inefficient way, but just sharing the solution, so it might help someone to come up with efficient solution.
Edit1:我以非常低效的方式做到了,但只是分享了解决方案,因此它可能会帮助某人提出有效的解决方案。
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;
}
回答by TGafford
First, let me provide credit to MiloYip at this link
首先,让我在此链接上感谢 MiloYip
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.
我认为这是一种非常干净/有效的方法。
回答by a.raya203
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:
这可以处理任何类型的 JSON 树。您所要做的就是传递一个值:
Value v = document.GetObject();
Value& m= v;
enter(m);
And you're done!
你完成了!
回答by Dawid Drozd
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;
并从文档开始: rapidjson::Document document;
for (auto it = document.MemberBegin(); it != document.MemberEnd(); ++it)
{
parseRecursive("", it, _values);
}
回答by Johann Studanski
a.raya203's post https://stackoverflow.com/a/43120359/6155053didn'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…
a.raya203的帖子https://stackoverflow.com/a/43120359/6155053对我来说开箱即用(不处理当前 Rapidjson 版本实现的所有类型,因此在例如输出时遇到错误gltfs 包含双打等)但它让我了解了rapidjson 如何解析文档,所以我想我会把我的(更新的)代码留在这里,也许它可以帮助其他人......
#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);
}
回答by bebenlebricolo
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()而是依靠可用于虚拟“拆分”文档链的指针:
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).
当我需要遍历我的文件时(面对数组时),这特别方便。