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

enyaitl3  于 2022-12-24  发布在  其他
关注(0)|答案(7)|浏览(174)

我正在尝试使用boost属性树创建JSON数组。
文档中写道:"JSON数组Map到节点。每个元素都是一个名称为空的子节点。"
所以我想创建一个空名称的属性树,然后调用write_json(...)来得到数组,但是文档没有告诉我如何创建未命名的子节点,我尝试了ptree.add_child("", value),但是得到了:

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

文档似乎没有提到这一点,至少我想不出有什么办法。有人能帮忙吗?

3npbholx

3npbholx1#

简单阵列:

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

x7rlezfr2#

你要做的就是找点乐子。这是我的记忆,但我喜欢这样。

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,其中几个我已经提交了bug报告,但没有得到回应:(
编辑:解决其序列化错误为{":“,":“}的问题
只有当数组是根元素时才会发生这种情况。boost ptree编写器将所有根元素都视为对象,而不是数组或值。这是由boost/propert_tree/detail/json_parser_writer.hpp中的以下行导致的

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

去掉“indent〉0 &&”将允许它正确地写入数组。
如果您不喜欢生成的空间大小,可以使用我提供的补丁here

egdjgwm8

egdjgwm83#

在开始使用属性树来表示JSON结构时,我遇到了类似的问题,但我没有解决。还要注意,从文档中可以看出,属性树并不完全支持类型信息:
JSON值被Map到包含该值的节点,但是所有类型信息都丢失了;数字以及文字“空”、“真”和“假”被简单地Map到它们的字符串形式。
了解了这些之后,我切换到了更完整的JSON实现JSON Spirit。这个库使用Boost Spirit进行JSON语法实现,完全支持包含数组的JSON。
我建议您使用另一种C++ JSON实现。

irlmq6kh

irlmq6kh4#

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

mzillmmw

mzillmmw5#

截至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])
ckocjqey

ckocjqey6#

如果你想在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';
}
2uluyalo

2uluyalo7#

对官方文件和以上回答感到困惑。以下是我的理解。
属性树由节点组成。
每个节点如下所示

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;
    }

相关问题