boost::property_tree::ptree

boost::property_tree::ptree是一种树结构,其本质相当于(注意这不是定义式):

1
2
3
4
struct basic_ptree{
string data;
basic_ptree* next;
};
可见其结构上就是一种树/链表,适合用于解析嵌套的键值对文件,如json、ini等。

json

json方法基本没有可参考性,看不看都行,真是脑子被踢了才会学。

json解析

json解析首选仍然是boost::json库,因为其支持类型检查、序列化等,ptree基本只能解析结构相对简单、且知道值类型的(或者类型比较统一的)json结构,了解基本接口即可:

其中get_child表示获取子节点,得到的是一个新的ptree对象,对于一般数据类型,遍历ptree,迭代器是一个pair对象:std::pair<string, property_tree::ptree>,若second为空,意味着无子节点(注意ptree.second.empty成立,指的是ptree.second.next为空,ptree本身是含string数据的,是不为空的,这就是为什么代码判断了空,还是继续使用second取值的原因),所以该second只能是int/double/string等。

对于getget_value都是取键值的方法,其容易混淆,例如我们存在键值对:

1
{outObject:{object : math}}
两种取值方法分别是:
1
2
3
4
for(const auto& pos : outObject){ //outObject是一个ptree对象
string math = outObject.get<string>(pos.first); //法一
string math = pos.second.get_value<string>(); //法二:当pos遍历到的是object键值对
}

以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <boost/json.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp> //for json
#include <boost/property_tree/ini_parser.hpp> //for ini
#include <sstream>

using namespace std;

namespace property_tree = boost::property_tree;
/*
{
"Company": "Committee",
"From": 1998,
"Name": "boost",
"Page": {
"Developers": "C++ Standard Committee",
"Download": "Download Link",
"Home": "Home Page"
},
"Version": [
1.0,
2.0,
3.0,
{"isTest": true}
]
}
*/

int main(){
property_tree::ptree root;
property_tree::read_json("D:/Documents/Desktop/test/jsonTest.json", root);

string company = root.get<string>("Company");
int from = root.get<int>("From");
string name = root.get<string>("Name");

property_tree::ptree page = root.get_child("Page");
string dev = page.get<string>("Developers");
string download = page.get<string>("Download");
string home = page.get<string>("Home");

stringstream ss;
ss << company << from << name << dev << download << home;
property_tree::ptree version = root.get_child("Version");
for(const auto& pos : version){ //auto类型是:std::pair<string, property_tree::ptree>
//对于array,pos.first总是为空的字符串
if(pos.second.empty()){ //子节点为空,second值就是double值,first为空字符串
double version_num = pos.second.get_value<double>();
ss << version_num;
}
else{
bool b = pos.second.get_value<bool>("isTest");
ss << b;
}
}

cout << ss.str() <<endl; //打印了键的每个值

cout << "done" <<endl;
return 0;
}

上述代码假设了类型关系和内容对解析者是清晰的,复杂度主要来自嵌套关系。当数据内容是不可见的,且有有限种数据类型,因为ptree的类型检查缺失,对于含多种类型的json,其遍历打印比较复杂,这就是为什么不推荐使用ptree管理复杂嵌套类型json的原因,你可以看出以下代码的努力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <boost/json.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp> //for json
#include <boost/property_tree/ini_parser.hpp> //for ini
#include <sstream>
#include <type_traits>

using namespace std;

namespace property_tree = boost::property_tree;
/*
{
"Company": "Committee",
"From": 9.99,
"Name": "boost",
"Page": {
"Developers": "C++ Standard Committee",
"Download": "Download Link",
"Home": "Home Page"
},
"Version": [
1.0,
2.0,
3.0,
{"isTest": true}
]
}
*/

//适配不同类型打印
template <typename T, typename = typename std::enable_if<std::is_same<T, double>::value || std::is_same<T, string>::value, T>::type>
bool can_convert(const property_tree::ptree& ptree, T& ret){
if(ptree.empty()){
try{
ret = ptree.get_value<T>();
return true;
}catch(std::exception& e){
return false;
}
}
}

int main(){
property_tree::ptree root;
property_tree::read_json("D:/Documents/Desktop/test/jsonTest.json", root);
vector<string> keyArray;
stringstream ss;
for(const auto& pos : root){
keyArray.push_back(pos.first);
if(pos.second.empty()){ //没有子节点,说明只能是object、array外的普通变量
double num;
if(can_convert(pos.second, num)){ //数据是string类型
num += 1;
ss << num;
}
else{
string str;
if(can_convert(pos.second, str)){ //数据是int类型
ss << str;
}
else{
cerr << "UnKnown Value Type!" << endl;
}
}
}//ss = Committee10.99boost

else{ //object或array
property_tree::ptree object = root.get_child(pos.first);
for(const auto& item : object){
if(!item.first.empty()){ //有键,为object
keyArray.push_back(item.first);
//string page_str = object.get<string>(item.first); //写法1
string page_str = item.second.get_value<string>(); //等效写法2
ss << page_str;
}
else{ //json Array
if(item.second.empty()){ //没有子节点,说明是Version Array中的double小数
ss << item.second.get_value<double>();
}
else{
for(const auto pp : item.second){
ss << pp.second.get_value(pp.first);
}
}
}
}
}
}
cout << "----------------Key : " <<endl;
for(const auto& pos : keyArray){
cout << pos << " ";
}
cout << endl <<"----------------End Key" <<endl;
cout << ss.str() << endl;
cout << "done" <<endl;
return 0;
}
输出:
1
2
3
4
5
----------------Key :
Company From Name Page Developers Download Home Version
----------------End Key
Committee10.99boostC++ Standard CommitteeDownload LinkHome Page123true
done

ini封装与解析

比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <boost/json.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp> //for ini
// #include <unistd.h> //for sync

using namespace std;

namespace property_tree = boost::property_tree;
/*
[database]
username = root
password = secret

[server]
ip = 192.168.1.1
port = 8080
*/

int main(){
//新建、修改、新增
property_tree::ptree ptree;
ptree.put("database.username", "root");
ptree.put("database.password", 1234);
ptree.put("server.ip", "192.168.1.1");
ptree.put("server.port", 8080);
property_tree::write_ini("D:/Documents/Desktop/test/test.ini", ptree);

//读取
property_tree::ptree rptree;
property_tree::read_ini("D:/Documents/Desktop/test/test.ini", rptree);

string username = ptree.get<string>("database.username");
cout << username <<endl;
username = "Eden";
//修改
ptree.put("database.username", username);

ptree.put("database.friend", "Mike");
property_tree::write_ini("D:/Documents/Desktop/test/test.ini", ptree);
//sync(); //only for linux

cout << "done" <<endl;
return 0;
}