Boost社区12月11日发布了1.75版本,在之前,Boost使用Boost.PropertyTree解析JSON,XML,INI和INFO格式的文件。但是由于成文较早及需要兼容其他的数据格式,相比较于其他的C++解析库,使用时不方便。
Boost.JSON相对于Boost.PropertyTree来所,其只能支持JSON格式的解析,但是其使用方法更为简便,直接。
有两种方法使用Boost.JSON,一种是动态链接库,此时引入头文件boost/json.hpp,同时链接对应的动态库;第二种是使用header only模式,此时只需要引入头文件boost/json/src.hpp即可。
数据类型
array
数组类型,用于储存JSON中的数组。实际使用的时候类似于std::vectorboost::json::value,差异极小。
object
object是JSON键值对的容器,对象类型,用于储存JSON中的对象。实际使用时类似于std::map<std::string, boost::json::value>,但是相对来说,它们之间的差异较大。定义在<boost/json/object.hpp>
项目 | Value |
---|---|
at(key) | 获取指定Key对应的元素的引用(不存在时会抛出out_of_range异常) |
begin/end | 获取iterator |
capacity | 容量 |
cbegin/cend | 获取const iterator |
clear | Erase all elements. |
contains(key) | 判断Key是否存在 |
count(key) | 返回Key的数量 |
emplace | Construct an element in-place. |
empty | 是否为空 |
erase(it/key) | 根据key或iterator移除元素 |
find(key) | 返回指定key的iterator或end()。 |
if_contains(key) | 返回key对应value的指针,或null(不存在时)。 |
insert | 插入元素 |
insert_or_assign | 插入或赋值(若key已存在) |
operator= | Copy assignment.Move assignment.Assignment. |
operator[] | 存在返回对应引用,若不存在则插入null value,并返回 |
reserve | 增加容量(若指定值小于现有容量,则什么也不做) |
size | 大小 |
swap | Swap two objects. |
max_size | 静态成员,返回object能保存元素的最大数量。 |
string
字符串类型,用于储存JSON中的字符串。实际使用时和std::basic_string类似,不过其只支持UTF-8编码,如果需要支持其他编码,在解码时候需要修改option中相应的选项。
value
表示JSON值的类型,可以储存任意类型,也可以变换为各种类型。其中有一些特色的函数比如as_object,get_array,emplace_int64之类的。它们的工作都类似,将boost::json::value对象转化为对应的类型。但是他们之间也有一定的区别。
构造json
{
"a_string" : "test_string",
"a_number" : 123,
"a_null" : null,
"a_array" : [1, "2", {"123" : "123"}],
"a_object" : {
"a_name": "a_data"
},
"a_bool" : true
}
构造的方法也很简单:定义一个object,然后设定各个value即可
boost::json::object val;
val["a_string"] = "test_string";
val["a_number"] = 123;
val["a_null"] = nullptr;
val["a_array"] = {
1, "2", boost::json::object({{"123", "123"}})
};
val["a_object"].emplace_object()["a_name"] = "a_data";
val["a_bool"] = true;
Boost.JSON支持使用std::initializer_list来构造自己的对象。所以也可以这样使用:
boost::json::value val2 = {
{"a_string", "test_string"},
{"a_number", 123},
{"a_null", nullptr},
{"a_array", {1, "2", {{"123", "123"}}}},
{"a_object", {{"a_name", "a_data"}}},
{"a_bool", true}
};
但是使用initializer_list构造时,有时很难区分是数组还是对象,可以明确指定
// 构造[["data", "value"]]
boost::json::value jsonAry = {boost::json::array({"data", "value"})};
// 构造{"data": "value"}
boost::json::value jsonObj = boost::json::object({{"data", "value"}});
序列化
生成了json对象以后,就可以使用serialize对对象进行序列化了。
std::cout << boost::json::serialize(val2) << std::endl;
除了直接把整个对象直接输出,Boost.JSON还支持分部分进行流输出,这种方法在数据量较大时,可以有效降低内存占用。
boost::json::serializer ser;
ser.reset(&val);
char temp_buff[6];
while (!ser.done()) {
std::memset(temp_buff, 0, sizeof(char) * 6);
ser.read(temp_buff, 5);
std::cout << temp_buff << std::endl;
}
如果缓存变量是数组,还可以直接使用ser.read(temp_buff)。
需要注意的是,ser.read并不会默认在字符串末尾加\0,所以如果需要直接输出,在输入时对缓存置0,同时为\0空余一个字符。
也可以直接使用输出的boost::string_view。
对象序列化
对象转换为JSON,Boost.JSON提供了一个非常简单的方法:只需要在需要序列化的类的命名空间中,定义一个重载函数tag_invoke(是类所在的命名空间,而不是在类里面定义),然后通过value_from即可方便地序列化对象了:
namespace NSJsonTest {
class MyClass {
public:
int a;
int b;
MyClass (int a = 0, int b = 1):
a(a), b(b) {}
};
void tag_invoke(boost::json::value_from_tag, boost::json::value &jv, MyClass const &c) {
auto & jo = jv.emplace_object();
jo["a"] = c.a;
jo["b"] = c.b;
}
MyClass myObj;
auto jv = boost::json::value_from(myObj)
}
其中,boost::json::value_from_tag是作为标签存在的,方便Boost.JSON分辨序列化函数的。jv是输出的JSON对象,c是输入的对象。
boost::json::value_from(MyObj)
使用的话,直接调用value_from函数即可。
序列化还有一个好处就是,可以在使用std::initializer_list初始化JSON对象时,直接使用自定义对象。譬如:
boost::json::value val = {MyObj};
这里的val是一个数组,里面包含了一个对象MyObj。
反序列化
使用boost::json::parse
auto decode_val = boost::json::parse("{\"123\": [1, 2, 3]}");
增加错误处理
boost::json::error_code ec;
boost::json::parse("{\"123\": [1, 2, 3]}", ec);
std::cout << ec.message() << std::endl;
boost::json::parse("{\"123\": [1, 2, 3}", ec);
std::cout << ec.message() << std::endl;
对象反序列化
与对象序列化对应的是对象反序列化;也是在命名空间中定义个tag_invoke函数,然后即可通过value_to把JSON对象反序列化为类对象了:
MyClass tag_invoke(boost::json::value_to_tag<MyClass>, boost::json::value const &jv) {
auto &jo = jv.as_object();
return MyClass(jo.at("a").as_int64(), jo.at("b").as_int64());
}
// jv为前面序列化时的对象
auto myObj = boost::json::value_to<MyClass>(jv);
需要注意的是,由于传入的jv是被const修饰的,所以不能类似于jv[“a”]使用。
使用也和上面的类似,提供了一个value_to<>模板函数。
auto MyObj = boost::json::value_to<MyNameSpace::MyClass>(vj);
无论是序列化还是反序列化,对于标准库中的容器,Boost.JSON都可以直接使用。
流输入
通过stream_parser可以流的方式读入要解析的字符串
boost::json::stream_parser p;
p.reset();
p.write("[1, 2,");
p.write("3]");
p.finish();
std::cout << boost::json::serialize(p.release()) << std::endl;
示例
json文件:
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <iostream>
#include <iterator>
#include <fstream>
namespace json = boost::json;
struct Rec {
int64_t number;
std::string string;
friend Rec tag_invoke(json::value_to_tag<Rec>, json::value const& v) {
auto& o = v.as_object();
return {
o.at("number").as_int64(),
boost::json::value_to<std::string>(o.at("string")),
};
}
friend void tag_invoke(json::value_from_tag, json::value& v, Rec const& rec)
{
v = json::object{
{"number", rec.number},
{"string", rec.string},
};
}
};
int main() {
std::ifstream ifs("../input.json");
std::string input(std::istreambuf_iterator<char>(ifs), {});
using Recs = std::vector<Rec>;
Recs recs = boost::json::value_to<std::vector<Rec>>(json::parse(input));
for (auto& [n, s] : recs) {
std::cout << "Rec { " << n << ", " << std::quoted(s) << " }\n";
// some frivolous changes:
n *= 2;
reverse(begin(s), end(s));
}
std::cout << "Modified json: " << json::value_from(recs) << "\n";
}
参考
Boost.JSON Boost的JSON解析库