72

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

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

所以我想创建一个名称为空的属性树,然后调用write_json(...)以获取数组。但是,文档并没有告诉我如何创建未命名的子节点。我试过ptree.add_child("", value)了,但这会产生:

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

该文档似乎没有解决这一点,至少我无法弄清楚。任何人都可以帮忙吗?

4

7 回答 7

119

简单数组:

#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"
        }
    ]
}

希望这可以帮助

于 2012-10-10T14:19:34.663 回答
22

你需要做的就是这个乐趣。这是来自记忆,但这样的事情对我有用。

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 解析和写入中存在一些错误。其中几个我已经提交了错误报告 - 没有回应:(

编辑:解决对它错误序列化为 {"":"","":""} 的担忧

这只发生在数组是根元素时。boost ptree 编写器将所有根元素视为对象 - 绝不是数组或值。这是由 boost/propert_tree/detail/json_parser_writer.hpp 中的以下行引起的

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

摆脱“缩进> 0 &&”将允许它正确地写入数组。

如果您不喜欢产生多少空间,可以使用我在此处提供的补丁

于 2010-01-22T02:29:18.770 回答
12

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

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

了解了这个之后,我切换到了更完整的 JSON 实现JSON Spirit。该库使用 Boost Spirit 实现 JSON 语法,并完全支持包括数组在内的 JSON。

我建议您使用替代的 C++ JSON 实现。

于 2010-01-22T09:21:52.650 回答
6

在我的情况下,我想将一个数组添加到一个或多或少的任意位置,因此,就像迈克尔的回答一样,创建一个子树并用数组元素填充它:

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()oradd_child()函数将整个子树添加到目标树中,就像这样......

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

put_child 函数采用路径和树作为参数,并将 arrayChild “嫁接”到 targetTree

于 2012-04-11T23:56:45.040 回答
0

对官方文档和上述答案感到困惑。以下是我的理解。

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

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

使用 'put' 将 'value' 放入数据中 使用
'push_back' 将 'node' 放入子项中\

// 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;
    }
于 2021-06-30T11:08:47.247 回答
0

如果您想要 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';
}
于 2019-09-30T16:26:15.553 回答
0

截至boost 1.60.0,问题仍然存在。

提供一种Python 3解决方法 ( Gist ),可以在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])
于 2018-10-29T11:02:30.093 回答