假设我有以下树:
boost::property_tree::ptree tree;
tree.add("1.2.3", "value-1");
tree.add("1.2.3.4", "nested-value");
tree.add("1.2.3", "value-2");
tree.add("1.2.33", "other-value");
具有以下序列化信息表单:
1
{
2
{
3 value-1
{
4 nested-value
}
3 value-2
33 other-value
}
}
是否有方法删除具有提供的(可能是嵌套的)路径的所有节点? 即:
remove(tree, "1.2.3");
BOOST_ASSERT(!tree.get_optional<std::string>("1.2.3") &&
!tree.get_child_optional("1.2.3"));
结果INFO表单:
1
{
2
{
33 other-value
}
}
查看ptree文档和源代码我发现了几种删除树的直接子项的方法(嵌套子项不计算)。此外,有几种方法可以通过它的完整路径获取子树(即使它是嵌套的)。但由于没有办法轻易获得节点的父节点,我无法将所有这些结合起来得到我需要的东西。
有没有简单的方法来获得我需要的东西,可能没有重新实现树遍历的需要?
答案 0 :(得分:2)
我不认为可以做到。它可以是JSON中的好东西,但是INFO子树键可以在每个级别重复,因此遍历所有树是很重要的
也许这个答案有助于开始:How to iterate over XML structure in boost::property_tree
非常小心关于迭代正在修改的树。您需要仔细检查erase
答案 1 :(得分:1)
如果有人需要,我就是这样做的(对不起,没有C ++ 11):
template <typename TTree>
boost::optional<TTree&> get_parent_optional(TTree& tree,
typename TTree::path_type path)
{
if (path.empty())
return boost::optional<TTree&>();
TTree::key_type root = path.reduce();
if (path.empty())
{
return (tree.find(root) != tree.not_found()) ?
boost::optional<TTree&>(tree) : boost::optional<TTree&>();
}
std::pair<TTree::assoc_iterator, TTree::assoc_iterator> range =
tree.equal_range(root);
for (TTree::assoc_iterator it = range.first; it != range.second; ++it)
{
boost::optional<TTree&> result = get_parent_optional(it->second, path);
if (result)
return result;
}
return boost::optional<TTree&>();
}
template <typename TTree>
boost::optional<TTree&> get_parent_optional(TTree& tree,
const typename TTree::key_type & path)
{
return get_parent_optional(tree, TTree::path_type(path));
}
template <typename TTree>
boost::optional<TTree&> get_parent_optional(TTree& tree,
const char * path)
{
return get_parent_optional(tree, std::string(path));
}
template <typename TTree>
typename TTree::key_type get_last_fragment(const typename TTree::key_type & keyPath)
{
TTree::path_type path(keyPath);
if (path.empty())
return TTree::key_type(); // or exception
while (!path.single())
path.reduce();
return path.reduce();
}
template <typename TTree>
void erase(TTree & tree, const typename TTree::key_type & path)
{
boost::optional<TTree&> parent;
typename TTree::key_type subkey = get_last_fragment<TTree>(path);
while (parent = get_parent_optional(tree, path))
{
parent->erase(subkey);
}
}
注意,如果要删除多个分支,则在每个分支删除后重复一个树。如果是大树,可能会出现问题。
答案 2 :(得分:0)
受Alex Che的回答启发,这是我的解决方案。它假设没有重复的密钥,也没有包含数据和子节点的密钥。它返回一个表示成功的bool。它还可以选择删除空的父键,而不是为键留下空值。
template <typename PTree>
bool erasePath(PTree &inout_tree,
const typename PTree::key_type &in_keyPath,
bool in_removeEmptyKeys)
{
try
{
PTree *subTree = &inout_tree;
typename PTree::path_type path(in_keyPath);
typename PTree::key_type parentPath;
typename PTree::key_type subKey;
while (!path.single())
{
subKey = path.reduce();
parentPath = parentPath.empty() ? subKey :
parentPath + path.separator() + subKey;
subTree = &(subTree->get_child(subKey));
}
subKey = path.reduce();
if ( subTree->erase(subKey) == 0 )
{
return false;
}
if (in_removeEmptyKeys && subTree->empty() && !parentPath.empty())
{
return erasePath(inout_tree, parentPath);
}
return true;
}
catch (std::exception &)
{
return false;
}
}