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解析库