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

时间:2010-01-22 01:58:14

标签: c++ json boost boost-propertytree

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

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

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

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

文档似乎没有解决这一问题,至少不能以任何方式解决。有人可以帮忙吗?

7 个答案:

答案 0 :(得分:97)

简单数组:

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

希望这会有所帮助

答案 1 :(得分:20)

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

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 writer将所有根元素视为对象 - 绝不是数组或值。这是由boost / properties_tree / detail / json_parser_writer.hpp中的以下行引起的

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

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

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

答案 2 :(得分:11)

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

  

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

在得知这一点后,我转而使用更完整的JSON实现JSON Spirit。该库使用Boost Spirit进行JSON语法实现,并完全支持包含数组的JSON。

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

答案 3 :(得分:6)

在我的情况下,我想将一个数组添加到一个或多或少的任意位置,因此,像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

答案 4 :(得分: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])

答案 5 :(得分:0)

如果您想要C ++中的JSON,则不需要Boost。借助此library,您可以将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';
}

答案 6 :(得分:0)

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

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

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

使用'put'将'value'放入数据中
将“节点”放入带有“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;
    }