简体   繁体   English

使用rapidjson迭代并检索JSON中的嵌套对象

[英]iterate and retrieve nested object in JSON using rapidjson

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(字符串缓冲区)不允许[],任何想法我怎么能这样做?

Edit1: I did it in very inefficient way, but just sharing the solution, so it might help someone to come up with efficient solution. 编辑1:我以非常低效的方式做到了,但只是共享解决方案,因此它可能有助于某人提出有效的解决方案。

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;
}

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. 我认为这是一种非常干净/高效的方法。

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. 这可以处理任何类型的JSON树。 All you have to do is pass a Value as such: 您所要做的就是传递一个Value:

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

And you're done! 而且你已经完成了!

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);
}

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 eg 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);
}

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: 而是依赖可用于虚拟“拆分”文档链的指针:

\ndoc["Node1"]["Node2"]["Node3"]...["NodeX"].GetType() 文档[ “节点1”] [ “节点2”] [ “节点3”] ... [ “节点X”]。的GetType()\n                     | | | |\n                 pointer1 | pointer1 |\n                                  pointer2 指针1\n(*pointer_on_Node_N)["Node N+1"] = doc["Node1"][...]["NodeN"]["Node N+1] (* pointer_on_Node_N)[“Node N + 1”] = doc [“Node1”] [...] [“NodeN”] [“Node N + 1]\n

That was particularly handy when I needed to iterate over my file (when facing arrays). 当我需要迭代我的文件(面向数组时)时,这特别方便。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM