当前位置: 首页 > article >正文

【Jsoncpp】manipulating JSON data in C++

源代码

在这里插入图片描述

#include <iostream>  // 引入输入输出流库,用于标准输入输出操作
#include <fstream>   // 引入文件流库,用于文件读写操作
#include <json/json.h>  // 引入JSON库,用于解析和操作JSON数据

using namespace std;  // 使用标准命名空间,避免每次调用标准库函数时都要加std::

int main()
{
    Json::Value root;  // 创建一个Json::Value对象,用于存储解析后的JSON数据
    ifstream ifs;  // 创建一个输入文件流对象,用于读取文件
    ifs.open("profile.json");  // 打开名为"profile.json"的文件

    Json::CharReaderBuilder builder;  // 创建一个JSON解析器的构建器对象
    builder["collectComments"] = true;  // 设置解析器在解析时收集注释

    JSONCPP_STRING errs;  // 创建一个字符串对象,用于存储解析过程中可能产生的错误信息

    // 使用解析器从文件流中解析JSON数据,并将结果存储在root对象中
    // 如果解析失败,输出错误信息并返回EXIT_FAILURE
    if (!parseFromStream(builder, ifs, &root, &errs))
    {
        std::cout << errs << std::endl;  // 输出错误信息
        return EXIT_FAILURE;  // 返回程序失败状态
    }

    Json::Value::iterator itr = root.begin();  // 创建一个迭代器,指向JSON对象的起始位置
    int i = 0;  // 初始化一个计数器,用于遍历JSON对象的成员
    string name;  // 创建一个字符串对象,用于存储JSON对象的成员名称

    // 遍历JSON对象的每个成员
    for (itr = root.begin(); itr != root.end(); itr++)
    {
        name = root.getMemberNames()[i];  // 获取当前成员的名称

        // 如果当前成员的值是一个数组(大小大于1),则遍历数组中的每个元素
        if (root[name].size() > 1) {
            for (auto x : root[name]) {
                cout << name << ": " << x << endl;  // 输出成员名称和数组中的每个元素
            }
        }
        else {
            cout << name + " ";  // 输出成员名称
            cout << root[name] << endl;  // 输出成员的值
        }
        i++;  // 计数器加1,准备处理下一个成员
    }
}

json内容为:

{"isbn": "123-456-789",
    "author":
        {
            "lastname": "Doe",
            "firstname" : "Jane"
        },
    "editor":
        {
            "lastname": "Smith",
            "firstname": "Jane"
        },
    "title": "The Ultimate Database Study Guide",
    "category": ["Non-Fiction", "Technology"]
}

在这里插入图片描述

输出结果:

author: “Jane”
author: “Doe”
category: “Non-Fiction”
category: “Technology”
editor: “Jane”
editor: “Smith”
isbn “123-456-789”
title “The Ultimate Database Study Guide”

为什么没有按json顺序呢?

JSON 是一种无序的数据结构,Json::Value 在解析 JSON 文件时并不会保留原始的顺序。因此,当你遍历 Json::Value 对象时,成员的顺序可能与 JSON 文件中的顺序不一致。

原因分析:

  1. JSON 的无序性

    • JSON 标准(RFC 8259)明确规定,JSON 对象中的键值对是无序的。解析器在解析 JSON 时,通常会使用某种数据结构(如哈希表)来存储键值对,而哈希表本身是无序的。
    • 因此,Json::Value 在解析 JSON 文件时,不会保留键值对的原始顺序。
  2. getMemberNames() 的顺序

    • 在你的代码中,root.getMemberNames() 返回的是 JSON 对象的所有键(成员名称),但它们的顺序可能与 JSON 文件中的顺序不同。
    • 这是因为 getMemberNames() 返回的顺序取决于 Json::Value 内部存储键值对的方式,而不是 JSON 文件中的顺序。
  3. 遍历顺序

    • 当你使用 for (itr = root.begin(); itr != root.end(); itr++) 遍历 Json::Value 时,迭代器的顺序也是由 Json::Value 内部决定的,可能与 JSON 文件中的顺序不一致。

如何保持顺序?

如果你需要保持 JSON 文件中的原始顺序,可以尝试以下方法:

方法 1:使用有序的 JSON 库

某些 JSON 库支持有序的 JSON 解析。例如,nlohmann::json(一个流行的 C++ JSON 库)可以保留 JSON 文件中的键值对顺序。

方法 2:手动指定顺序

如果你知道 JSON 文件的结构,可以手动指定遍历的顺序。例如:

std::vector<std::string> keys = {"isbn", "author", "editor", "title", "category"};
for (const auto& key : keys) {
    if (root.isMember(key)) {
        if (root[key].isArray()) {
            for (const auto& item : root[key]) {
                std::cout << key << ": " << item << std::endl;
            }
        } else {
            std::cout << key << " " << root[key] << std::endl;
        }
    }
}
方法 3:修改代码逻辑

如果你希望按照 JSON 文件中的顺序输出,可以修改代码逻辑,直接按照 getMemberNames() 的顺序输出,而不是依赖迭代器:

for (size_t i = 0; i < root.size(); ++i) {
    std::string name = root.getMemberNames()[i];
    if (root[name].isArray()) {
        for (const auto& item : root[name]) {
            std::cout << name << ": " << item << std::endl;
        }
    } else {
        std::cout << name << " " << root[name] << std::endl;
    }
}

为什么输出结果中嵌套对象被拆开?

在你的输出结果中,authoreditor 被拆开为多个键值对,这是因为代码中对嵌套对象的处理逻辑有问题。具体来说:

  • root[name] 是一个嵌套对象时,代码没有正确处理嵌套对象的结构,而是直接将其拆开输出。
修正代码:

如果你希望正确输出嵌套对象的结构,可以修改代码逻辑,递归处理嵌套对象。例如:

void printJsonValue(const Json::Value& value, const std::string& name = "") {
    if (value.isObject()) {
        for (const auto& key : value.getMemberNames()) {
            printJsonValue(value[key], name.empty() ? key : name + "." + key);
        }
    } else if (value.isArray()) {
        for (const auto& item : value) {
            printJsonValue(item, name);
        }
    } else {
        std::cout << name << " " << value << std::endl;
    }
}

int main() {
    Json::Value root;
    std::ifstream ifs("profile.json");
    Json::CharReaderBuilder builder;
    JSONCPP_STRING errs;

    if (!parseFromStream(builder, ifs, &root, &errs)) {
        std::cout << errs << std::endl;
        return EXIT_FAILURE;
    }

    printJsonValue(root);
    return 0;
}
修正后的输出:
isbn "123-456-789"
author.lastname "Doe"
author.firstname "Jane"
editor.lastname "Smith"
editor.firstname "Jane"
title "The Ultimate Database Study Guide"
category "Non-Fiction"
category "Technology"

总结:

  1. JSON 是无序的,解析后的顺序可能与文件中的顺序不一致。
  2. 如果需要保持顺序,可以使用有序的 JSON 库或手动指定顺序。
  3. 嵌套对象的处理需要递归遍历,否则会拆开输出。

http://www.kler.cn/a/471891.html

相关文章:

  • PHP进阶-在Ubuntu上搭建LAMP环境教程
  • /src/utils/request.ts:axios 请求封装,适用于需要统一处理请求和响应的场景
  • 浙江安吉成新的分布式光伏发电项目应用
  • unity学习12:地图相关的一些基础2, 增加layer种草种树
  • 如何用代码提交spark任务并且获取任务权柄
  • nlp培训重点-2
  • 时间格式转换
  • 软件开发为什么要用CI/CD方法
  • 《量子比特大阅兵:不同类型量子比特在人工智能领域的优劣势剖析》
  • 如何制作一份好的年终总结PPT?-中小企实战运营和营销工作室博客
  • RAFT:随机退火森林
  • playwright 录制
  • QT 下拉菜单设置参数 起始端口/结束端口/线程数量 端口扫描4
  • C#异步多线程——ThreadPool线程池
  • 分享:osgb倾斜数据转cesium-3dtiles 小工具.
  • 计算机网络之---有线网络的传输介质
  • STM32-WWDG/IWDG看门狗
  • 海陵HLK-TX510人脸识别模块 stm32使用
  • 常见的开源网络操作系统
  • 如何很快将文件转换成另外一种编码格式?编码?按指定编码格式编译?如何检测文件编码格式?Java .class文件编码和JVM运行期内存编码?
  • 关于Mac中的shell
  • RP2K:一个面向细粒度图像的大规模零售商品数据集
  • 使用ML.NET进行对象检测
  • opencv摄像头标定程序实现
  • Go语言的语法
  • 会员制营销与门店业绩提升:以开源AI智能名片S2B2C商城小程序为例的深度剖析