使用 rapidjson 在 JSON 中迭代和检索嵌套对象

iterate and retrieve nested object in JSON using rapidjson

我正在解析一个 JSON 结构,它类似于以下内容

{
    "item1" : "value1"
    "item2" : "value2"
    // ...
    "itemn" : {
        "outernestedItem1" : {
            "innerNestedItem1" : "valuen1"
            "innerNestedItem2" : "valuen2"
        }
        // ....
        "outernestedItemn" : {
            "innerNestedItem1" : "valuen1"
            "innerNestedItem2" : "valuen2"
        }
    }
}

外部嵌套项的数量不固定,所以我使用 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();
}

但是 sb(字符串缓冲区)不允许使用 [],知道我该怎么做吗?

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

首先,让我在此link

感谢MiloYip

其次——这是我为我的项目所做的:

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

我认为这是一个很好的 clean/efficient 方法。

这是我最近在做的事情:

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

这可以处理任何类型的 JSON 树。您所要做的就是传递一个值:

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

大功告成!

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

并从文档开始:rapidjson::Document document;

for (auto it = document.MemberBegin(); it != document.MemberEnd(); ++it)
{
    parseRecursive("", it, _values);
}

a.raya203 的 post 对我来说开箱即用(不处理当前 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, ' '));
    }
};

像使用它

#include "3rdParty/rapidjson/document.h"

rapidjson::Document document;
{
    document.Parse(FileHelper::ReadString(filePath)->c_str());
}

if (!document.HasParseError())
{
    JsonNodePrinter::PrintNode(document, 4);
}

我最近也在想同样的问题,这是我得到的:

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

我多次使用这个技巧来避免使用像

 doc["Node1"]["Node2"]["Node3"]...["NodeX"].GetType() 
这样无休止的长访问请求 而是依赖可用于虚拟 "split" 文档链的指针:

doc["Node1"]["Node2"]["Node3"]...["NodeX"].GetType()
                     |            |
                 pointer1         |
                                  pointer2
(*pointer_on_Node_N)["Node N+1"] = doc["Node1"][...]["NodeN"]["Node N+1]

当我需要遍历我的文件时(面对数组时),这特别方便。