在 Boost 中使用属性树创建 JSON 数组

我正在尝试使用提升属性树创建一个 JSON 数组。

文件表示: “ JSON 数组映射到节点。每个元素是一个名称为空的子节点。”

因此,我想创建一个名称为空的属性树,然后调用 write_json(...)来获取数组。但是,文档并没有告诉我如何创建未命名的子节点。我尝试了 ptree.add_child("", value),但是结果是:

Assertion `!p.empty() && "Empty path not allowed for put_child."' failed

文档似乎没有提到这一点,至少没有以我能想到的任何方式提到。有人能帮忙吗?

74375 次浏览

你要做的就是找点乐子。这是我的记忆,但这样的东西对我很有用。

boost::property_tree::ptree root;
boost::property_tree::ptree child1;
boost::property_tree::ptree child2;


// .. fill in children here with what you want
// ...


ptree.push_back( std::make_pair("", child1 ) );
ptree.push_back( std::make_pair("", child2 ) );

但是要注意 json 解析和编写中有几个 bug。其中有几个我已经提交了错误报告-没有回应: (

编辑: 解决关于错误序列化为{“ :”,“ :”}的问题

只有当数组是根元素时才会发生这种情况。Bostptree 编写器将所有根元素视为对象——而不是数组或值。这是由于 Boost/property _ tree/Details/json _ parser _ writer 中的下面一行引起的。Hpp

else if (indent > 0 && pt.count(Str()) == pt.size())

去掉“ indent > 0 & &”将允许它正确地编写数组。

如果你不喜欢产生多少空间,你可以使用我提供的补丁 给你

当开始使用 Property Tree 表示 JSON 结构时,我遇到了类似的问题,但我没有解决。还要注意,从文档中可以看出,属性树并不完全支持类型信息:

JSON 值映射到包含该值的节点。但是,所有类型信息都丢失了; 数字以及文字“ null”、“ true”和“ false”只是映射到它们的字符串形式。

了解了这一点之后,我切换到更完整的 JSON 实现 JSON Spirit。该库将 Boost Spirit 用于 JSON 语法实现,并完全支持包含数组的 JSON。

我建议您使用另一种 C + + JSON 实现。

在我的例子中,我想将一个数组添加到一个或多或少任意的位置,因此,像 Michael 的回答一样,创建一个子树并用数组元素填充它:

using boost::property_tree::ptree;


ptree targetTree;
ptree arrayChild;
ptree arrayElement;


//add array elements as desired, loop, whatever, for example
for(int i = 0; i < 3; i++)
{
arrayElement.put_value(i);
arrayChild.push_back(std::make_pair("",arrayElement))
}

填充完子树后,使用 put_child()add_child()函数将整个子树添加到目标树,如下所示..。

targetTree.put_child(ptree::path_type("target.path.to.array"),arrayChild)

Put _ child 函数接受参数的路径和树,并将“嫁接”arrayChild 到 targetTree 中

简单数组:

#include <boost/property_tree/ptree.hpp>
using boost::property_tree::ptree;


ptree pt;
ptree children;
ptree child1, child2, child3;


child1.put("", 1);
child2.put("", 2);
child3.put("", 3);


children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));


pt.add_child("MyArray", children);


write_json("test1.json", pt);

结果:

{
"MyArray":
[
"1",
"2",
"3"
]
}

对象上的数组:

ptree pt;
ptree children;
ptree child1, child2, child3;




child1.put("childkeyA", 1);
child1.put("childkeyB", 2);


child2.put("childkeyA", 3);
child2.put("childkeyB", 4);


child3.put("childkeyA", 5);
child3.put("childkeyB", 6);


children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));


pt.put("testkey", "testvalue");
pt.add_child("MyArray", children);


write_json("test2.json", pt);

结果:

{
"testkey": "testvalue",
"MyArray":
[
{
"childkeyA": "1",
"childkeyB": "2"
},
{
"childkeyA": "3",
"childkeyB": "4"
},
{
"childkeyA": "5",
"childkeyB": "6"
}
]
}

希望这能帮上忙

至于 boost 1.60.0,问题依然存在。

提供一个 Python 3变通方案(要点) ,可以在 boost::property_tree::write_json之后对其进行系统调用。

#!/usr/bin/env python3




def lex_leaf(lf: str):
if lf.isdecimal():
return int(lf)
elif lf in ['True', 'true']:
return True
elif lf in ['False', 'false']:
return False
else:
try:
return float(lf)
except ValueError:
return lf


def lex_tree(j):
tj = type(j)
if tj == dict:
for k, v in j.items():
j[k] = lex_tree(v)
elif tj == list:
j = [lex_tree(l) for l in j]
elif tj == str:
j = lex_leaf(j)
else:
j = lex_leaf(j)
return j




def lex_file(fn: str):
import json
with open(fn, "r") as fp:
ji = json.load(fp)
jo = lex_tree(ji)
with open(fn, 'w') as fp:
json.dump(jo, fp)




if __name__ == '__main__':
import sys
lex_file(sys.argv[1])

如果您希望在 C + + 中使用 JSON,则不需要 Boost。使用这个 图书馆,您可以将 JSON 作为第一类数据类型,其行为类似于 STL 容器。

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


// Or treat is as an STL container; create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);


// also use emplace_back
j.emplace_back(1.78);


// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << '\n';
}

与官方文件和上述答案混淆。 下面是我所了解的。

属性树由节点组成。
每个节点如下所示

 struct ptree
{
map<key_name,value>          data;
vector<pair<key_name,ptree>> children;
};

在数据中使用“ put”将“ value”放入数据中
将‘ node’放入具有‘ push _ back’的子级中

// Write
bt::ptree root;
bt::ptree active;
bt::ptree requested;
bt::ptree n1, n2, n3;
        

n1.put("name", "Mark");
n1.put("age", 20);
n1.put("job", "aaa");


n2.put("name", "Rosie");
n2.put("age", "19");
n2.put("job", "bbb");


n3.put("name", "sunwoo");
n3.put("age", "10");
n3.put("job", "ccc");


active.push_back   ({ "",l1 });
active.push_back   ({ "",l2 });
requested.push_back({ "",l3 });
root.push_back     ({"active", active});
root.push_back     ({"requested", requested});


bt::write_json("E:\\1.json", root);




// READ
bt::ptree root2;
bt::ptree active2;
bt::ptree requested2;
bt::ptree r1, r2, r3;


bt::read_json("E:\\1.json", root2);


// loop children
for (auto& [k,n] : root.get_child("active"))
{
cout << n.get<string>("name", "unknown");
cout << n.get<int>   ("age" , 11);
cout << n.get<string>("job" , "man");
cout << endl << flush;
}