You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中基于RapidJSON的JSON比较功能实现遇阻,求技术帮助

解决RapidJSON实现JSON比较的问题

嘿,我明白你在实现JSON比较功能时卡在Document d3这块儿了——咱们一步步拆解问题,把这个功能补全。首先,JSON比较的核心是递归遍历两个JSON的所有节点,逐个对比类型和值;而你提到的d3,推测应该是用来记录差异结果的(如果只是判断是否相等,其实它不是必须的,但如果要生成差异报告就很有用)。

完整实现代码

先给你补全test类的代码,包含递归对比的核心逻辑和d3的初始化/差异记录思路:

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include "include/rapidjson/document.h"
#include "include/rapidjson/writer.h"
#include "include/rapidjson/prettywriter.h"
#include "include/rapidjson/stringbuffer.h"

using namespace std;
using namespace rapidjson;

class test {
private:
    // 辅助递归函数:对比两个Value节点,同时记录差异到diffDoc
    static bool isEqualValue(const Value& v1, const Value& v2, string& tempPath, Document& diffDoc) {
        // 先检查类型是否一致
        if (v1.GetType() != v2.GetType()) {
            // 记录类型差异到diffDoc
            Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
            Value diffObj(kObjectType);
            diffObj.AddMember("type_mismatch", true, diffDoc.GetAllocator());
            diffObj.AddMember("expected_type", v2.GetType(), diffDoc.GetAllocator());
            diffObj.AddMember("actual_type", v1.GetType(), diffDoc.GetAllocator());
            diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
            
            cout << "Type mismatch at path: " << tempPath << endl;
            return false;
        }

        switch (v1.GetType()) {
            case kObjectType: {
                // 对比对象成员数量
                if (v1.MemberCount() != v2.MemberCount()) {
                    Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
                    Value diffObj(kObjectType);
                    diffObj.AddMember("member_count_mismatch", true, diffDoc.GetAllocator());
                    diffObj.AddMember("expected_count", v2.MemberCount(), diffDoc.GetAllocator());
                    diffObj.AddMember("actual_count", v1.MemberCount(), diffDoc.GetAllocator());
                    diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                    
                    cout << "Member count mismatch at path: " << tempPath << endl;
                    return false;
                }
                // 遍历所有成员
                for (auto it1 = v1.MemberBegin(); it1 != v1.MemberEnd(); ++it1) {
                    string key = it1->name.GetString();
                    auto it2 = v2.FindMember(key.c_str());
                    if (it2 == v2.MemberEnd()) {
                        Value pathVal((tempPath.empty() ? key : tempPath + "." + key).c_str(), diffDoc.GetAllocator());
                        Value diffObj(kObjectType);
                        diffObj.AddMember("missing_member", true, diffDoc.GetAllocator());
                        diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                        
                        cout << "Missing member: " << tempPath << "." << key << endl;
                        return false;
                    }
                    string newPath = tempPath.empty() ? key : tempPath + "." + key;
                    if (!isEqualValue(it1->value, it2->value, newPath, diffDoc)) {
                        return false;
                    }
                }
                return true;
            }
            case kArrayType: {
                // 对比数组长度
                if (v1.Size() != v2.Size()) {
                    Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
                    Value diffObj(kObjectType);
                    diffObj.AddMember("array_size_mismatch", true, diffDoc.GetAllocator());
                    diffObj.AddMember("expected_size", v2.Size(), diffDoc.GetAllocator());
                    diffObj.AddMember("actual_size", v1.Size(), diffDoc.GetAllocator());
                    diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                    
                    cout << "Array size mismatch at path: " << tempPath << endl;
                    return false;
                }
                // 遍历数组元素
                for (SizeType i = 0; i < v1.Size(); ++i) {
                    string newPath = tempPath.empty() ? "[" + to_string(i) + "]" : tempPath + "[" + to_string(i) + "]";
                    if (!isEqualValue(v1[i], v2[i], newPath, diffDoc)) {
                        return false;
                    }
                }
                return true;
            }
            case kStringType: {
                if (strcmp(v1.GetString(), v2.GetString()) != 0) {
                    Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
                    Value diffObj(kObjectType);
                    diffObj.AddMember("string_mismatch", true, diffDoc.GetAllocator());
                    diffObj.AddMember("expected", v2.GetString(), diffDoc.GetAllocator());
                    diffObj.AddMember("actual", v1.GetString(), diffDoc.GetAllocator());
                    diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                    
                    cout << "String mismatch at path: " << tempPath << endl;
                    return false;
                }
                return true;
            }
            case kNumberType: {
                bool isEqual = false;
                if (v1.IsInt() && v2.IsInt()) {
                    isEqual = (v1.GetInt() == v2.GetInt());
                } else if (v1.IsDouble() && v2.IsDouble()) {
                    isEqual = (v1.GetDouble() == v2.GetDouble());
                } else if (v1.IsUint() && v2.IsUint()) {
                    isEqual = (v1.GetUint() == v2.GetUint());
                }

                if (!isEqual) {
                    Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
                    Value diffObj(kObjectType);
                    diffObj.AddMember("number_mismatch", true, diffDoc.GetAllocator());
                    // 简化处理:把数字转成字符串存储
                    char buf[64];
                    sprintf(buf, "%g", v2.IsInt() ? (double)v2.GetInt() : v2.GetDouble());
                    diffObj.AddMember("expected", buf, diffDoc.GetAllocator());
                    sprintf(buf, "%g", v1.IsInt() ? (double)v1.GetInt() : v1.GetDouble());
                    diffObj.AddMember("actual", buf, diffDoc.GetAllocator());
                    diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                    
                    cout << "Number mismatch at path: " << tempPath << endl;
                    return false;
                }
                return true;
            }
            case kBoolType: {
                if (v1.GetBool() != v2.GetBool()) {
                    Value pathVal(tempPath.c_str(), diffDoc.GetAllocator());
                    Value diffObj(kObjectType);
                    diffObj.AddMember("bool_mismatch", true, diffDoc.GetAllocator());
                    diffObj.AddMember("expected", v2.GetBool(), diffDoc.GetAllocator());
                    diffObj.AddMember("actual", v1.GetBool(), diffDoc.GetAllocator());
                    diffDoc.AddMember(pathVal, diffObj, diffDoc.GetAllocator());
                    
                    cout << "Bool mismatch at path: " << tempPath << endl;
                    return false;
                }
                return true;
            }
            case kNullType: {
                return true;
            }
            default:
                cout << "Unsupported JSON type at path: " << tempPath << endl;
                return false;
        }
    }

public:
    static bool isEqual(const string &item1, const string &item2, const string &temp) {
        // 解析第一个JSON,检查解析错误
        Document d1;
        if (d1.Parse(item1.c_str()).HasParseError()) {
            cout << "Failed to parse first JSON: error code " << d1.GetParseError() 
                 << " at offset " << d1.GetErrorOffset() << endl;
            return false;
        }

        // 解析第二个JSON,检查解析错误
        Document d2;
        if (d2.Parse(item2.c_str()).HasParseError()) {
            cout << "Failed to parse second JSON: error code " << d2.GetParseError() 
                 << " at offset " << d2.GetErrorOffset() << endl;
            return false;
        }

        // 初始化d3为差异结果文档
        Document d3;
        d3.SetObject();
        string path = temp.empty() ? "" : temp;
        
        // 调用递归对比函数,同时把差异写入d3
        bool isEqual = isEqualValue(d1, d2, path, d3);

        // 如果存在差异,格式化输出d3的内容
        if (!isEqual) {
            StringBuffer buffer;
            PrettyWriter<StringBuffer> writer(buffer);
            d3.Accept(writer);
            cout << "\n差异详情:\n" << buffer.GetString() << endl;
        }

        return isEqual;
    }
};

// 测试用例
int main() {
    string json1 = R"({"name":"Alice","age":30,"hobbies":["reading","hiking"],"active":true})";
    string json2 = R"({"name":"Alice","age":30,"hobbies":["reading","hiking"],"active":true})";
    string json3 = R"({"name":"Bob","age":25,"hobbies":["gaming"],"active":false})";

    cout << "=== 对比json1和json2 ===" << endl;
    bool result1 = test::isEqual(json1, json2, "");
    cout << "结果: " << boolalpha << result1 << "\n" << endl;

    cout << "=== 对比json1和json3 ===" << endl;
    bool result2 = test::isEqual(json1, json3, "");
    cout << "结果: " << boolalpha << result2 << endl;

    return 0;
}

关键部分解释

  1. 解析错误检查:原代码缺少JSON解析失败的处理,这里添加了HasParseError()的判断,能快速定位输入JSON的格式问题。
  2. 递归对比逻辑:isEqualValue函数是核心,它会根据JSON节点的类型(对象、数组、字符串等)做针对性对比,确保每个嵌套节点都能被覆盖。
  3. d3的作用:这里把d3作为差异结果文档,每当发现两个JSON节点不相等时,就把差异信息(路径、类型/值差异)写入d3,最后可以用PrettyWriter格式化输出差异详情。
  4. 路径追踪:通过tempPath参数记录当前对比的节点路径,方便快速定位差异位置。

运行效果

测试用例中,对比json1和json2会返回true,对比json1和json3会返回false,同时输出详细的差异路径和信息,比如缺少的成员、值不匹配的节点等。

内容的提问来源于stack exchange,提问作者Bishal Sahoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:30:16