一.认识json

JSON(JavaScrip Object Notation)是一种轻量级的数据交换格式。

可以精简为一句话:Json是一种数据格式。和语言无关,在什么语言中都可以使用Json。基于这种通用的数据格式,一般处理两方面的任务:

  • 组织数据(数据序列化),用于数据的网络传输
  • 组织数据(数据序列化),写磁盘文件实现数据的持久化存储(一般以.json作为文件后缀)

Json中主要有两种数据格式:Json数组和Json对象,并且这两种格式可以交叉嵌套使用,下面依次介绍下这两种数据格式:

二.json使用

1.json数组

Json数组使用 [] 表示,[]里边是元素,元素和元素之间使用逗号间隔,最后一个元素后边没有逗号,一个Json数组中支持同时存在多种不同类型的成员,包括:整形、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null。由此可见Json数组比起C/C++数组要灵活很多。

  • Json数组中的元素数据类型一致
// 整形
[1,2,3,4,5]
// 字符串
["luffy", "sanji", "zoro", "nami", "robin"]
  • Json数组中的元素数据类型不一致
[12, 13.34, true, false, "hello,world", null]
  • Json数组中的数组嵌套使用
[
    ["cat", "dog", "panda", "beer", "rabbit"],
    ["北京", "上海", "天津", "重庆"],
    ["luffy", "boy", 19]
]
  • Json数组和对象嵌套使用
[
    {
        "luffy":{
            "age":19,
            "father":"Monkey·D·Dragon",
            "grandpa":"Monkey D Garp",
            "brother1":"Portgas D Ace",
            "brother2":"Sabo"
        }
    }
]

2.json对象

Json对象使用 {} 来描述,每个Json对象中可以存储若干个元素,每一个元素对应一个键值对(key:value 结构),元素和元素之间使用逗号间隔,最后一个元素后边没有逗号。对于每个元素中的键值对有以下细节需要注意:

  • 键值(key)必须是字符串,位于同一层级的键值不要重复(因为是通过键值取出对应的value值)
  • value值的类型是可选的,可根据实际需求指定,可用类型包括:整形、 浮点、 字符串、 布尔类型、 json数组、 json对象、 空值-null

使用Json对象描述一个人的信息:

{
    "Name":"Ace",
    "Sex":"man",
    "Age":20,
    "Family":{
        "Father":"Gol·D·Roger",
        "Mother":"Portgas·D·Rouge",
        "Brother":["Sabo", "Monkey D. Luffy"]
    },
    "IsAlive":false,
    "Comment":"yyds"
}

3.注意事项

  • json对象或json数组中的元素之间用,隔开,最后一个元素不需要,
  • json对象中元素的键必须为字符串且不重复
  • 重点:在一个Json文件中只能有一个Json数组或者Json对象的根节点,不允许同时存储多个并列的根节点。

错误的写法:

// test.json
{
    "name":"luffy",
    "age":19
}
{
    "user":"ace",
    "passwd":"123456"
}

错误原因:在一个Json文件中有两个并列的Json根节点(并列包含Json对象和Json对象、Json对象和Json数组、Json数组和Json数组),根节点只能有一个。

正确的写法:

// test.json
{
    "Name":"Ace",
    "Sex":"man",
    "Age":20,
    "Family":{
        "Father":"Gol·D·Roger",
        "Mother":"Portgas·D·Rouge",
        "Brother":["Sabo", "Monkey D. Luffy"]
    },
    "IsAlive":false,
    "Comment":"yyds"
}

三.json理解(自己慢慢摸索)

1.json常见库

C++要想使用json,必须开始导入对应的头目和库文件,常用的C++对应库文件有以下几种:

  1. RapidJSON:
  • 这是一个非常流行的C++ JSON库,它提供了快速且易于使用的接口。
  • 导入头文件的方式通常是将RapidJSON的头文件包含到您的项目中。例如:
#include "rapidjson/document.h"
  1. nlohmann/json:
  • 这个库以其简洁的API和良好的文档而受到许多开发者的喜爱。
  • 导入头文件的方式是:
#include "nlohmann/json.hpp"
  1. jsoncpp:
  • jsoncpp是一个功能丰富的库,支持多种数据类型和流操作。
  • 导入头文件的方式通常是:
#include "json/json.h"
  1. sonic-cpp:
  • 这是由字节跳动自研的高性能JSON库,它利用向量化指令和优化内存布局来提高性能。
  • 导入头文件的方式可能需要查看该库的具体文档来确定。
  1. jsonxx:
  • 这是一个轻量级的JSON库,支持JSON解析和序列化。
  • 导入头文件的方式可能是:
#include "jsonxx/jsonxx.h"

2.json代码操作

这里我们选择第二种库文件,即nlohmann/json,假设在已导入对应头目和库文件的基础下:

json数组:

json empty_array_explicit = json::array();                       //创建一个空的json数组

empty_array_explicit.push_back(1); // 添加一个整数
empty_array_explicit.push_back("example"); // 添加一个字符串
empty_array_explicit.push_back(true); // 添加一个布尔值

json对象:

json s;                                                    //默认创建的是一个空的json对象

//第一种添加元素方式
s["key3"] = true;     // 添加一个布尔值
s["key4"] = {1, 2, 3}; // 添加一个数组
s["key5"] = {{"subkey1", "subvalue1"}, {"subkey2", "subvalue2"}}; // 添加一个嵌套对象

//第二种直接定义方式
json j2 = {
  {"pi", 3.141},
  {"happy", true},
  {"name", "Niels"},
  {"nothing", nullptr},
  {"answer", {
    {"everything", 42}
  }},
  {"list", {1, 0, 2}},
  {"object", {
    {"currency", "USD"},
    {"value", 42.99}
  }}
};

3.静态成员函数

对于 json empty_array_explicit = json::array(); 这行代码,它创建了一个空的 JSON 数组。这里的 json::array() 是一个静态成员函数,它返回一个空的 JSON 数组。

nlohmann/json 中,json 类提供了几个静态成员函数,这些函数允许您直接创建特定类型的 JSON 值,而不需要先创建一个 json 对象。这些静态成员函数包括:

  • json::object(): 创建一个空的 JSON 对象。
  • json::array(): 创建一个空的 JSON 数组。
  • json::null(): 创建一个 JSON null 值。
  • json::parse(std::string const&): 解析 JSON 字符串。
  • json::parse(std::istream&): 从输入流中解析 JSON 数据。
  • json::string(): 创建一个 JSON 字符串。
  • json::number_integer()json::number_unsigned(): 创建整数和无符号整数类型的 JSON 数值。
  • json::boolean(): 创建布尔类型的 JSON 值。

注意,可以以json()的类构造函数可以使用初始化列表的方式,建立json对象:

json object_with_data = json({
  {"key1", "value1"},
  {"key2", 42},
  {"key3", true}
});

4.序列化和反序列化

通过字符串创建json对象(反序列化):

json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;

nlohmann/json 库中,"_json" 是一个后缀操作符,它用于将字符串字面量转换为 json 对象。这个后缀操作符是 nlohmann/json 库特有的,它允许您直接从字符串字面量创建 json 对象,而不需要调用解析函数。

所以,这行代码的意思是:

  1. 创建一个字符串字面量:"{ \"happy\": true, \"pi\": 3.141 }"
  2. 使用 _json 后缀操作符将这个字符串字面量转换为 json 对象。
  3. 将转换后的 json 对象赋值给变量 j

字符串字面量 "{ \"happy\": true, \"pi\": 3.141 }" 中的反斜杠 \ 用于转义双引号 "。在 JSON 字符串中,双引号用于标识字符串的开始和结束,因此它们是特殊字符。当您需要在 JSON 字符串内部包含双引号时,您必须使用转义序列 \" 来表示一个双引号字符。

如果,用上C++11的原始字符变量,可以改写成:

auto j2 = R"(
  {
    "happy": true,
    "pi": 3.141
  }
)"_json;

获取 JSON 值的字符串表示形式(序列化):

std::string s = j.dump();    // {"happy":true,"pi":3.141}

std::cout << j.dump(4) << std::endl;           //这里的dump(4)中的4表示4个空格一个缩进
  1. j.dump() 方法被调用来将 json 对象 j3 序列化(转换)回 JSON 字符串格式。dump 方法返回一个 std::string 类型的值,包含了序列化的 JSON 数据。
  2. 这个序列化的 JSON 字符串被赋值给变量 s。在这个例子中,s 的值将是 "{\"happy\":true,\"pi\":3.141}",这是一个 JSON 格式的字符串,表示一个对象,其中 "happy" 的值是布尔值 true"pi" 的值是数值 3.141

5.成员方法

以下是一些常用的 json 类成员方法:

  1. .dump():
  • json 对象序列化为 JSON 字符串。可以指定格式化选项,如缩进。
  1. .parse():
  • 解析 JSON 字符串,创建对应的 json 对象。
  1. .get_ref():
  • 获取对 json 对象内部数据的引用,这在需要直接修改 json 对象的底层数据时很有用。
  1. .at(size_type index):
  • 通过索引访问 JSON 数组中的元素。如果索引无效,会抛出异常。
  1. .at(key):
  • 通过键名访问 JSON 对象中的元素。如果键不存在,会抛出异常。
  1. [size()]:
  • 用于访问 JSON 数组中的元素,类似于下标操作符。
  1. ["key"]:
  • 用于访问 JSON 对象中的元素,通过键名。如果键不存在,会创建一个新元素。
  1. .erase(key):
  • 删除 JSON 对象中的指定键。
  1. .push_back(value):
  • 向 JSON 数组中添加一个新元素。
  1. .emplace_back(key, value):
  • 在 JSON 数组的末尾就地构造一个新元素。
  1. .emplace(key, value):
  • 在 JSON 对象中就地构造一个新元素。
  1. .dump(std::ostream&):
  • 将 JSON 对象序列化并输出到提供的输出流。
  1. .dump(std::ostream&, int indent):
  • 将 JSON 对象序列化并输出到提供的输出流,indent 参数用于指定缩进级别。
  1. .dump(std::string&, int indent):
  • 将 JSON 对象序列化到字符串中,indent 参数用于指定缩进级别。
  1. .type():
  • 返回 json 对象的类型。
  1. .is_null(), .is_boolean(), .is_number(), .is_string(), .is_array(), .is_object():
  • 分别用于检查 json 对象是否为 null、布尔值、数值、字符串、数组或对象。

这些方法提供了对 JSON 数据的全面操作,包括创建、解析、访问、修改和序列化。

6.到/从流

int main() {
    json j;
    std::cin >> j;
    std::cout << std::setw(4) << j << std::endl;          //设置输出流的宽度是4个字符串
    return 0;
}

四.配置json环境

以下是将 nlohmann/json 库集成到 Visual Studio 2022 的步骤:

  1. 下载库文件
  1. 解压文件
  • 解压 include.zip 文件到您的工程文件夹下,或者任何您希望存放库文件的位置。
  1. 修改工程属性
  • 在 Visual Studio 2022 中,打开您的项目。
  • 右键点击项目名称,选择 "Properties"(属性)。
  • 在 "Configuration Properties"(配置属性)下,选择 "C/C++",然后点击 "General"(常规)。
  • 在 "Additional Include Directories"(附加包含目录)中,添加解压后的 include 目录的路径。例如,如果您将 include 目录解压到了 C:\libs\nlohmann_json,则添加 C:\libs\nlohmann_json\include
  1. 测试
  • 在您的源代码文件中,添加以下代码来测试库是否正确导入:
#include <iostream>
#include "nlohmann/json.hpp"

using json = nlohmann::json;

int main() {
    json j;
    // 首先创建一个空的 json 对象
    j["pi"] = 3.141;
    // 然后通过名称/值对的方式进行初始化
    std::cout << j.dump(4) << std::endl;
    return 0;
}
  • 编译并运行您的项目,如果一切正常,您应该能看到 JSON 对象被正确创建和打印。