如何通过递归使用Property Tree/RapidJSON解析深层嵌套JSON并实现JSON比较?
一、递归解析深层嵌套JSON的实现
JSON本身就是天然的树形结构,递归是处理这种深层嵌套最直观的方式——跟着层级往下钻,遇到分支(对象/数组)就递归处理,遇到叶子(基本类型)就直接处理。下面分别用Boost.Property Tree和RapidJSON来实现:
1. 用Boost.Property Tree实现递归解析
Boost.Property Tree(简称PTree)会把JSON解析成树状的ptree对象,我们只需要遍历每个节点,遇到子树就递归调用自己:
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #include <iostream> void parseNestedPTree(const boost::property_tree::ptree& tree, int depth = 0) { // 用缩进体现层级,看起来更清晰 std::string indent(depth * 2, ' '); for (const auto& pair : tree) { const std::string& key = pair.first; const boost::property_tree::ptree& node = pair.second; // 如果是叶子节点(没有子节点),直接输出键值 if (node.empty()) { std::cout << indent << key << ": " << node.get_value<std::string>() << std::endl; } else { // 非叶子节点,先打印键名,再递归解析子树 std::cout << indent << key << ": {" << std::endl; parseNestedPTree(node, depth + 1); std::cout << indent << "}" << std::endl; } } } int main() { boost::property_tree::ptree root; boost::property_tree::read_json("nested_data.json", root); parseNestedPTree(root); return 0; }
2. 用RapidJSON实现递归解析
RapidJSON的Value类型区分了JSON的所有数据类型,递归时需要先判断当前值的类型,再做对应处理:
#include "rapidjson/document.h" #include <iostream> using namespace rapidjson; void parseNestedRapidJSON(const Value& value, int depth = 0) { std::string indent(depth * 2, ' '); switch (value.GetType()) { case kObjectType: { // 对象类型:遍历所有成员,递归处理每个成员的值 for (Value::ConstMemberIterator itr = value.MemberBegin(); itr != value.MemberEnd(); ++itr) { std::cout << indent << itr->name.GetString() << ": "; parseNestedRapidJSON(itr->value, depth + 1); std::cout << std::endl; } break; } case kArrayType: { // 数组类型:遍历所有元素,递归处理每个元素 std::cout << indent << "[" << std::endl; for (Value::ConstValueIterator itr = value.Begin(); itr != value.End(); ++itr) { parseNestedRapidJSON(*itr, depth + 1); std::cout << std::endl; } std::cout << indent << "]"; break; } // 处理基本类型:直接输出值 case kStringType: std::cout << value.GetString(); break; case kNumberType: std::cout << value.GetDouble(); break; case kTrueType: std::cout << "true"; break; case kFalseType: std::cout << "false"; break; case kNullType: std::cout << "null"; break; default: std::cout << "unknown type"; } } int main() { const char* json_str = R"({"user":"Bob","profile":{"age":30,"tags":["programmer","hiker"]}})"; Document doc; doc.Parse(json_str); parseNestedRapidJSON(doc); return 0; }
核心逻辑就是类型判断+递归:遇到对象/数组就深入下一层,基本类型直接输出,完美贴合JSON的嵌套结构。
二、equalJson函数代码逻辑分析
先把你给出的代码片段补全(结合上下文推测完整逻辑):
static bool equalJson(Value &json1, Value &json2, Value &reference, bool a) { for (Value::ConstMemberIterator itr = reference.MemberBegin(); itr <= reference.MemberEnd(); itr++) { if (itr->value.GetType() == kArrayType) { for (auto itr1 = itr->value.Begin(); itr1 != itr->value.End(); itr1++) { // cout<<itr->name.GetString()<<endl;//获取顶层为数组的键或成员 switch (itr1->GetType()) { case kObjectType: // 遍历数组中的对象成员,递归校验 for (auto sub_itr = itr1->MemberBegin(); sub_itr != itr1->MemberEnd(); ++sub_itr) { if (!equalJson(json1[sub_itr->name.GetString()], json2[sub_itr->name.GetString()], *sub_itr, a)) { return false; } } break; // 其他数组元素类型的处理逻辑(比如字符串、数字) } } } // 其他reference成员类型的处理逻辑(比如单个对象、基本类型) } // 所有指定字段都匹配则返回true return true; }
核心设计意图
这个函数不是完全比较两个JSON的所有内容,而是基于reference的模板,校验json1和json2在指定字段和层级上是否匹配——相当于只关心reference里定义的部分,其他字段不管。
逐段逻辑拆解
参数说明:
json1/json2:需要校验的两个JSON对象reference:校验模板,定义了需要检查的字段、结构bool a:推测是个控制开关(比如是否严格匹配空值、是否忽略类型差异),具体要看完整实现
遍历reference的顶层成员:
for (Value::ConstMemberIterator itr = reference.MemberBegin(); itr <= reference.MemberEnd(); itr++)❗这里有个潜在bug:RapidJSON的
MemberEnd()是尾后迭代器,正确的遍历判断应该是itr != reference.MemberEnd(),用<=会导致越界访问。处理reference中的数组成员:
如果reference的某个成员是数组,就遍历数组里的每个元素,再根据元素类型做处理。递归处理数组中的对象元素:
当数组元素是对象时,遍历这个对象的所有成员,递归调用equalJson,深入到下一层校验json1和json2对应的字段——只要有一个字段不匹配,就直接返回false。
❗原代码里内层循环用了和外层一样的变量名itr,这会覆盖外层迭代器,属于明显的代码bug,我改成了sub_itr。
整体流程总结
函数通过递归遍历reference的结构,逐层校验json1和json2在对应位置的内容:
- 遇到对象/数组就递归深入下一层
- 一旦发现指定字段不匹配,立即返回
false - 所有指定字段都匹配则返回
true
内容的提问来源于stack exchange,提问作者Pheonix

