RapidJSON - 使用通配符遍历具有不同成员名称的复杂 JSON 结构

RapidJSON - using wildcards to iterate through complex JSON structures with different member names

我有一个复杂的 JSON 要加载到 C++11 中的数据结构中,我得到了关于 RapidJSON 的高度推荐。我需要遍历一个复杂的 JSON 并四处寻找有关如何做的答案。我找到的最佳答案是 this thread.

但是,在将此解决方案与我的解决方案相匹配时出现了一个小故障,我在 JSON 中的成员名称不同但内容相同:

"responsibilities": {
  "starters_reciepe": {
    "name": "bok choi salad",
    "type": "veggie",
    "ingredients": {
      "leafyIng": "bok choi",
      "proteinIng": "tofu",
      "seasoning": [
        {
          "2 tsp": "salt",
          "1 tsp": "turmric"
        }
      ]
    }
  },
  "mainCourse_reciepe": {
    "name": "pad tai",
    "type": "yum yum",
    "ingredients": {
      "leafyIng": "chard",
      "proteinIng": "soylent green"
      "seasoning": [
        {
          "2 tsp": "black pepper",
          "1 tsp": "tears of the angels"
        }
      ]
    }
}

}

基本上,我需要检查一下成分的含量,但我无法忘记 starters_reciepe 不像 mainCourse_reciepe 的事实。

已编辑: 这是我的代码:

Document d;

ifstream in("TestingJSON.json", ios::binary);
if (!in)
    throw runtime_error("Failed to open file");

istreambuf_iterator<char> head(in);
istreambuf_iterator<char> tail;
string data(head, tail);

d.Parse(data.c_str());

const Value& prog = d["responsibilities"];
for (Value::ConstValueIterator p = prog.Begin(); p != prog.End(); ++p) {
cout << (*p)["iUniqueID"].GetString()<<endl;
    const Value& inFiles = (*p)["inFiles"];
    for (Value::ConstValueIterator inFile = inFiles.Begin(); inFile != prog.End(); ++inFile) {
        cout << (*inFile)["sFileType"].GetString() << endl;
        cout << (*inFile)["pos"]["x1"].GetInt() << endl;
    }
}

我可以使用通配符并写入 *_reciepe 吗?

我可以在 RapidJSON 和通配符上找到任何东西。这甚至有可能吗?

如果您只需要检查一个字符串是否以已知值结尾,那么在没有通配符库的情况下直接比较它是非常简单的:

auto& obj = doc["responsibilities"];
std::string suffix = "_reciepe";
for (auto p = obj.MemberBegin(); p != obj.MemberEnd(); ++p) {
    auto& member_name = p->name;
    if (member_name.GetStringLength() >= suffix.length()) {
        if (memcmp(member_name.GetString() + member_name.GetStringLength() - suffix.length(), suffix.c_str(), suffix.length()) == 0) {
            // Process matching node
            std::cout << p->value["name"].GetString() << std::endl;
        }
    }
}

如果您需要匹配更复杂的模式,那么您可以使用 std::regex

始终使用 linters(例如 https://jsonlint.com/)验证原始 JSON。您问题中的 JSON 无效。你需要解决这个问题。

您 JSON 中的 "responsibilites" 对象仅包含食谱。我不确定您为什么需要将它与 *_recipe 进行比较。但是,鉴于下面的示例,如果需要,您可以轻松实现该比较。 thread 在这方面可能会有所帮助。


您可以对这些迭代使用 C++11 基于范围的 for 循环。根据您的用例,只需注意您想要 use/manipulate 的正确类型。如有疑问,请参阅 rapidjson 的 tutorial 和文档。

这是一个原始文字字符串作为 JSON 输入的示例:

#include <iostream>
#include <rapidjson/document.h>

int main()
{
    constexpr auto data = R"json(
    {
      "responsibilities": {
          "starters_recipe": {
              "name": "bok choi salad",
              "type": "veggie",
              "ingredients": {
                  "leafyIng": "bok choi",
                  "proteinIng": "tofu",
                  "seasoning": [{
                      "2 tsp": "salt",
                      "1 tsp": "turmric"
                  }]
              }
          },
          "mainCourse_recipe": {
              "name": "pad tai",
              "type": "yum yum",
              "ingredients": {
                  "leafyIng": "chard",
                  "proteinIng": "soylent green",
                  "seasoning": [{
                      "2 tsp": "black pepper",
                      "1 tsp": "tears of the angels"
                  }]
              }
          }
      }
    }
    )json";

    rapidjson::Document doc;
    doc.Parse( data );

    const auto& courses = doc["responsibilities"].GetObject();
    for ( const auto& course : courses )
    {
        const auto& course_name = course.name.GetString();
        const auto& recipe      = courses[course_name].GetObject();
        const auto& recipe_name = recipe["name"].GetString();
        const auto& ingredients = recipe["ingredients"].GetObject();
        const auto& leafyIng    = ingredients["leafyIng"].GetString();
        const auto& proteinIng  = ingredients["proteinIng"].GetString();
        const auto& seasoning   = ingredients["seasoning"].GetArray()[0].GetObject();

        std::cout << "Course: " << course_name << '\n'
                  << "Recipe: " << recipe_name << '\n'
                  << "Ingredients:\n"
                  << "- Leaf     : " << leafyIng << '\n'
                  << "- Protein  : " << proteinIng << '\n'
                  << "- Seasoning:\n";

        for ( const auto& s : seasoning )
        {
            const auto& k = s.name.GetString();
            const auto& v = s.value.GetString();
            std::cout << "  - " << k << ", " << v << '\n';
        }

        std::cout << '\n';
    }

    return 0;
}

输出:

Course: starters_recipe
Recipe: bok choi salad
Ingredients:
- Leaf     : bok choi
- Protein  : tofu
- Seasoning:
  - 2 tsp, salt
  - 1 tsp, turmric

Course: mainCourse_recipe
Recipe: pad tai
Ingredients:
- Leaf     : chard
- Protein  : soylent green
- Seasoning:
  - 2 tsp, black pepper
  - 1 tsp, tears of the angels

"seasoning" 数组只包含一个对象,这就是为什么这一行引用第 0 个索引:

const auto& seasoning = ingredients["seasoning"].GetArray()[0].GetObject();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^

我猜你想要一个对象数组,而不是一个包含单个对象的数组。

这个:

"seasoning": [   
     { "2 tsp": "black pepper" },
     {  "1 tsp": "tears of the angels" }
]

而且,不是这个:

"seasoning": [{    
      "2 tsp": "black pepper",
      "1 tsp": "tears of the angels"
}]

您还必须在代码中相应地操作它。