nlohmann-json库使用简介

2023-10-27

nlohmann-json库使用简介

GitHub地址 nlohmann/json

简介

JSON库千千万,为何nlohmann/json库那么热门呢?原因在于它的设计理念非常人性化:

  • 提供符合直觉的语法:可以像STL容器一样来使用,提供了丰富而且符合直觉的接口;
  • 简易的导入方式:仅头文件(header-only)形式的JSON库,方便整合到项目中;
  • 严谨的测试:简而言之就是不用担心库的解析出问题,准确率贼高。

缺点也是有的:

  • 不追求内存的效率;
  • 不追求性能;

因此,在不需要高性能的场景下,使用这个库来解析和生成JSON文件是很方便的。如果需要高性能,还是老老实实用rapidjson吧。

基本用例

先从官方的简易用例开始

生成JSON结构的数据

若需要生成如下结构的JSON数据

{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
    "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
    "currency": "USD",
    "value": 42.99
  }
}

只需要这样编写代码:

#include <nlohmann/json.hpp>
using namspace nlohmann;
// 构造一个json对象(null)
json j;

// 添加一个json对象,key为pi,值为double类型的3.141
j["pi"] = 3.141;

// 以 bool 形式存储一个布尔值
j["happy"] = true;

// 以 std::string 的形式存储一个字符串对象
j["name"] = "Niels";

// 通过nullptr向key为nothing的对象中添加一个json对象
j["nothing"] = nullptr;

// 在一个json对象中添加一个json对象
j["answer"]["everything"] = 42;

// 以 std::vector 形式保存一个数组(使用std::initializer_list)
j["list"] = { 1, 0, 2 };

// 添加另一个json对象(以pair的形式)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };

通过gdb调试来看json对象的变化
调试
甚至可以用大括号初始化的方式来一步到位初始化JSON数据结构;

json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};

输入输出JSON文件

输入输出JSON文件可以简单地用std::fstream来做

  • 简易的输出
    std::fstream ofs("output.json", std::ios::out);
    ofs << j.dump();
  • 简易的输入加遍历方式(C++17)
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>

using namespace nlohmann;

int main(int argc, char** argv)
{
    if (std::fstream ifs("output.json", std::ios::in); ifs)
    {
        json j = json::parse(ifs);
        for (auto const& item : j.items())
        {
            std::cout << item << '\n';
        }
    }
    return 0;
}

从STL容器转换到JSON

这里就直接贴官方样例了,很好理解

std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]

std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
// [1.2, 2.3, 3.4, 5.6]

std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
// [true, true, false, true]

std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]

std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// [1, 2, 3, 4]

std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set); // only one entry for "one" is used
// ["four", "one", "three", "two"]

std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset); // only one entry for "one" is used
// maybe ["two", "three", "four", "one"]

std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]

std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]

std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
// {"one": 1, "three": 3, "two": 2 }

std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
json j_umap(c_umap);
// {"one": 1.2, "two": 2.3, "three": 3.4}

std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_mmap(c_mmap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}

std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_ummap(c_ummap); // only one entry for key "three" is used
// maybe {"one": true, "two": true, "three": true}

自定义结构体转换函数(序列化/反序列化)

假设我们现在有一个这样的结构需要序列化为JSON格式的文本:

#include <fstream>
#include <nlohmann/json.hpp>
#include <string>
#include <vector>

using namespace nlohmann;
using nlohmann::json;

namespace wb
{
struct Asset
{
    std::string name;
    int value;
};

struct Person
{
    std::string name;
    int age;
    bool married;
    std::vector<std::string> habbits;
    std::vector<Asset> assets;
};

void to_json(json &j, const Asset &a)
{
    j = json{{"name", a.name}, {"value", a.value}};
}

void to_json(json &j, const Person &p)
{
    j = json{{"name", p.name}, {"age", p.age}, {"married", p.married}};
    j["habbits"] = p.habbits;
    for (auto const &a : p.assets)
    {
        json ja;
        to_json(ja, a);
        j["assets"].emplace_back(std::move(ja));
    }
}

}  // namespace wb

针对结构体中的每个结构都需要写一个to_json函数用于转换(这个函数的名字可以任取,不一定是to_json)。
需要注意的是,对于每个自定义的结构,都需要自行提供to_json函数的实现。
使用以下代码进行测试:

int main(int argc, char **argv)
{
    wb::Person wb = {"wb",
                     18,
                     true,
                     {"football", "reading"},
                     {{"house", 9999}, {"car", 8888}}};

    if (std::fstream ofs("output.json", std::ios::out); ofs)
    {
    	json j;
    	wb::to_json(j, wb);

    	ofs << j.dump(4) << '\n'; // 缩进为4的JSON格式
	}
    return 0;
}

可以得到输出的output.json文件如下:

{
    "age": 18,
    "assets": [
        {
            "name": "house",
            "value": 9999
        },
        {
            "name": "car",
            "value": 8888
        }
    ],
    "habbits": [
        "football",
        "reading"
    ],
    "married": true,
    "name": "wb"
}

读取JSON文件,反序列化的例子如下:

// 头文件和结构体定义略
void from_json(const json& j, Asset& a)
{
    j.at("name").get_to(a.name);
    j.at("value").get_to(a.value);
}

void from_json(const json& j, Person& p)
{
    j.at("name").get_to(p.name);
    j.at("age").get_to(p.age);
    j.at("married").get_to(p.married);
    j.at("habbits").get_to(p.habbits);
    for (auto const& ja : j.at("assets"))
    {
        Asset a;
        from_json(ja, a);
        p.assets.emplace_back(std::move(a));
    }
}
// 为了方便验证结果,加入了一个naive的运算符重载
bool operator==(const Person& lhs, const Person& rhs)
{
    return lhs.name == rhs.name;
}

}  // namespace wb

在完成了from_json函数后(同样不一定需要命名为from_json,可以是其他名字),就可以轻易地对JSON格式的文本文件反序列化从而得到一个结构体了。
使用下面的代码进行测试:

int main(int argc, char** argv)
{
    wb::Person wb = {"wb",
                     18,
                     true,
                     {"football", "reading"},
                     {{"house", 9999}, {"car", 8888}}};
    if (std::fstream ifs("output.json", std::ios::in); ifs)
    {
        json j = json::parse(ifs);
        wb::Person pj;
        from_json(j, pj);
        assert(wb == pj);
        std::cout << "same" << std::endl;
    }
    return 0;
}

运行结果打印"same",说明pjwb两个结构体是"相等"的(只比较了name字段,这显然这不严谨,但是为了演示简便就省略了过多的代码)。

总结

至此,我们就掌握了nlohmann/json库的常用方法了。其实还有很多特殊的技巧,但是不一定常用,有兴趣的读者可以自己探索。
顺便提一下CMake项目中引用nlohmann/json库的方法:

find_package(nlohmann_json 3.2.0 REQUIRED)
add_library(foo ...)
target_link_libraries(foo PRIVATE nlohmann_json::nlohmann_json)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

nlohmann-json库使用简介 的相关文章

  • 将运算符 << 添加到 std::vector

    我想添加operator lt lt to std vector
  • Exit() 时是否调用基本对象析构函数?

    我意识到这个问题已经出现过几次 但我试图获得上述问题的明确答案 但我不断遇到相互矛盾的信息 我需要知道的是 当我使用 exit 时 基本类对象是否被破坏 我知道需要删除动态内存 但我的意思更像是 include
  • Rx.NET 中是否有一个Subject 实现,其功能类似于BehaviourSubject,但仅在值发生更改时才发出?

    有没有Subject https learn microsoft com en us previous versions dotnet reactive extensions hh229699 v vs 103 Rx NET 中的实现在功能
  • 转换 const void*

    我有一个函数返回一个const void 我想用它的信息作为char 我可以将它投射为 C 风格的罚款 char variable但是当我尝试使用reinterpret cast like reinterpret cast
  • 当事件button.click发生时,如何获取按钮名称/标签?

    我以编程方式制作按钮并将它们添加到堆栈面板中 以便每次用户导航到页面时按钮都会发生变化 我正在尝试做这样的事情 当我单击创建的按钮时 它将获取按钮的标签并转到正确的页面 但是 我无法使用 RoutedEventHandler 访问按钮元素
  • 传递 constexpr 对象

    我决定给予新的C 14的定义constexpr旋转并充分利用它 我决定编写一个小的编译时字符串解析器 然而 我正在努力保持我的对象constexpr将其传递给函数时 考虑以下代码 include
  • java中如何重新初始化int数组

    class PassingRefByVal static void Change int pArray pArray 0 888 This change affects the original element pArray new int
  • 有些有助于理解“产量”

    在我不断追求少吸的过程中 我试图理解 产量 的说法 但我不断遇到同样的错误 someMethod 的主体不能是迭代器块 因为 System Collections Generic List 不是迭代器接口类型 这是我被卡住的代码 forea
  • 如何将 .txt 文件中的数据转换为 xml? C#

    我在一个文本文件中有数千行数据 我想通过将其转换为更容易搜索的内容来轻松搜索 我希望 XML 或其他类型的大型数据结构 尽管我不确定它是否是最好的对于我的想法 每行的数据如下所示 第 31 册 托马斯 乔治 32 34 154 每本书都不是
  • 处理右值时的 insert 与 emplace

    std string myString std unordered set
  • 不同 C++ 文件中的相同类名

    如果两个 C 文件具有相同名称的类的不同定义 那么当它们被编译和链接时 即使没有警告也会抛出一些东西 例如 a cc class Student public std string foo return A void foo a Stude
  • C++中判断unicode字符是全角还是半角

    我正在编写一个终端 控制台 应用程序 该应用程序应该包装任意 unicode 文本 终端通常使用等宽 固定宽度 字体 因此要换行文本 只需计算字符数并观察单词是否适合一行并采取相应的操作 问题是 Unicode 表中的全角字符在终端中占用了
  • 如何将AVFrame转换为glTexImage2D使用的纹理?

    如您所知 AVFrame 有 2 个属性 pFrame gt data pFrame gt linesize 当我从视频 sdcard test mp4 android平台 读取帧后 并将其转换为RGB AVFrame副 img conve
  • 从 C# 使用 Odbc 调用 Oracle 包函数

    我在 Oracle 包中定义了一个函数 CREATE OR REPLACE PACKAGE BODY TESTUSER TESTPKG as FUNCTION testfunc n IN NUMBER RETURN NUMBER as be
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • Visual Studio 2015:v120 与 v140?

    仅供参考 Win10 x64 我今天开始尝试 Visual Studio 2015 在弄清楚如何运行 C C 部分后 我尝试加载一个大型个人项目 该项目使用非官方的glsdk http glsdk sourceforge net docs
  • 在 Win32 控制台应用程序中设置光标位置

    如何在 Win32 控制台应用程序中设置光标位置 最好 我想避免制作句柄并使用 Windows 控制台功能 我花了整个早上沿着那条黑暗的小巷跑 它产生的问题比它解决的问题还要多 我似乎记得当我在大学时使用 stdio 做这件事相对简单 但我
  • 没有“对 *this”功能的右值引用的解决方法

    我有一个围绕可移动对象的代理容器类 并希望代理能够隐式生成对底层对象的右值引用 但仅当代理本身被移动时 我相信我将能够按照提案 n2439 实施此行为 将移动语义扩展到 this http www open std org jtc1 sc2
  • 为什么空循环使用如此多的处理器时间?

    如果我的代码中有一个空的 while 循环 例如 while true 它将把处理器的使用率提高到大约 25 但是 如果我执行以下操作 while true Sleep 1 它只会使用大约1 那么这是为什么呢 更新 感谢所有精彩的回复 但我
  • 当用户更改 Windows 中的语言键盘布局时如何通知?

    I want to show a message to user when the user changes the language keyboard layout of Windows for example from EN to FR

随机推荐

  • MyBatis 后端对数据库进行操作

    目录 1 MyBatis 是什么 2 MyBatis 的重要性 3 MyBatis 查询 3 1 创建数据库和表 3 2 添加MyBatis框架 持 3 2 1 新项目添加MyBatis 3 2 1 老项 添加 MyBatis 3 3 配置
  • 基于特征的图像配准方法

    From 基于特征的图像配准方法 基于特征的图像配准过程 1 特征空间 基于特征的配准方法在进行图像预处理从图像中提取所选的特征之前 要先确定特征空间 通常 特征空间的选择要考虑下面五个因素 相似性 参考图像与待配准图像中要匹配的特征应该是
  • js创建全0数组

    1 创建一个长度为m的全0数组 var arr new Array m fill 0 2 创建一个m行n列的全0数组 var arr new Array m fill new Array n fill 0
  • 一些笔试题笔记1

    总结一些常见的笔试题 最小公约数和最大公倍数 这个其实很简单 记住概念就可以了 我们利用辗转相除法计算 最大公约数 摘自百度百科 一般地 如果求a和b的最大公约数 a gt b 那么 当时 得 这里表示b整除a 而表示b不能整除 当时 设余
  • PostgreSQL分区

    修改表 官网手册 表分区 官网手册 概述 分区是指将一个逻辑上大的表拆分为较小的物理块 分区可以提供以下好处 在某些情况下 尤其是当表中大多数被频繁访问的行位于单个分区或少量分区中时 查询性能可以得到显着提高 分区替代了索引的前几列 从而减
  • [物联网方案-2]:传感器LoRa无线采集终端通用模型

  • 组网学习之什么是链路冗余(二)

    为什么要有链路冗余 一 设计冗余的目的 提高可靠性 通信时一条路不通走另一条路即冗余链路 采用具有冗余的核心层 分布层和接入层 试图消除网络中的单点故障 二 实施冗余的注意事项 1 MAC数据库不稳定 MAC地址表中的内容不稳定性源于交换机
  • ElasticSearch学习5-- 使用RestClient查询文档

    1 查询基本步骤 1 创建SearchRequest对象 2 准备Request source 也就是DSL QueryBuilders来构建查询条件 传入Request source 的 query 方法 3 发送请求 得到结果 4 解析
  • lol老是闪退到桌面_lol闪退到桌面怎么解决

    LOLwin10闪退到桌面解决方法是 右键点击桌面底部任务栏的空白位置 然后在弹出菜单中选择 任务管理器 菜单项 依次点击 文件 运行新任务 菜单项 在窗口中输入命令gpedit msc 依次点 击 计算机配置 管理模板 Windows组件
  • web.xml顺序不对错误(The content of element type “web-app“ must match “)

    web xml顺序不对错误 如果web xml提示以下错误 The content of element type web app must match icon display name description distributable
  • Linux CentOS安装抓包解包工具Wireshark图形化界面

    1 Wireshark介绍 Wireshark 是一个开源的网络协议分析工具 它能够捕获和分析网络数据包 提供深入的网络故障排除 网络性能优化和安全审计等功能 它支持跨多个操作系统 包括 Windows macOS 和 Linux 2 Wi
  • 基本配置文件配置

    一个具有JWT的项目 最基本的配置文件 在resource中 一般其实是有四个配置文件和一个有关Mybatis的xml包 一 服务器的配置 设置端口号 编码格式 二 设置有关spring的相关配置 控制器以及profile的配置 三 配置M
  • win服务器隐藏版本信息,Apache防盗链和隐藏版本信息

    文章目录 隐藏 一 防盗链 二 隐藏版本信息 实验要求 三台虚拟机分别是 linux和两台windows虚拟机 linux虚拟机为服务器 Windows7 1为客户端 Windows7 2为盗链端 实验步骤 一 防盗链 1 把httpd a
  • [Pytorch系列-47]:工具集 - torchvision.transforms.Normalize和ToSensor的深入详解

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121300054 目录 第1章 关于标
  • tensor2tensor自定义问题,训练模型(bpe篇)

    tensor2tensor自定义问题 训练模型 上一篇 https blog csdn net hpulfc article details 81172498 之前一篇文章简单介绍了如何使用 google 的SubwordTokenEnco
  • c++小项目:基于STL的演讲比赛流程管理系统

    一 项目目的 运用c 实现一个基于STL的演讲比赛流程管理系统 比赛方式 共两轮 第一轮为分组淘汰赛 第二轮为决赛 共有十名评委 打分方式为去掉最高分和最低分的平均分为基准 第一轮共两组 每组六人 为随机分组和抽签决定演讲顺序 每组取前三名
  • Linux下实现DNS的分离解析

    Linux下实现DNS的分离解析 一 DNS简介 什么是DNS DNS的基本概念 二 安装DNS解析Bind服务 BIND bind配置文件 安装bind bind chroot软件包 查看安装软件生成的配置文件 三 DNS分离解析案例 一
  • Java之Class.forName方法详解

    一 前言 二 案例 三 详解 一 前言 在说明Class类的静态方法forName 之前 先清楚有关Class类的几个概念 1 Class类封装了类或接口的运行时状态 Java程序在运行时 Java运行时系统一直对所有的对象进行所谓的运行时
  • 一些C++、Qt实用技巧

    一 RAII统计函数耗时 RAII 也称为 资源获取就是初始化 是c 等编程语言常用的管理资源 避免内存泄露的方法 它保证在任何情况下 使用对象时先构造对象 最后析构对象 经典使用场景 避免死锁 class MyLock public My
  • nlohmann-json库使用简介

    nlohmann json库使用简介 GitHub地址 nlohmann json 简介 JSON库千千万 为何nlohmann json库那么热门呢 原因在于它的设计理念非常人性化 提供符合直觉的语法 可以像STL容器一样来使用 提供了丰