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"
}]
您还必须在代码中相应地操作它。
我有一个复杂的 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"
}]
您还必须在代码中相应地操作它。