[英]Boost property tree: Remove a nested node
假設我有以下樹:
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");
它具有以下序列化的INFO形式:
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文檔和源代碼我發現了幾種刪除樹的直接子項的方法(嵌套子項不計算)。 此外,有幾種方法可以通過它的完整路徑獲取子樹(即使它是嵌套的)。 但由於無法輕松獲取節點的父節點,我無法將所有這些結合起來以獲得我需要的內容。
有沒有簡單的方法來獲得我需要的東西,可能沒有重新實現樹遍歷的需要?
我不認為可以做到。 它會很好 - 在JSON中可以,但是INFO子樹鍵可以在每個級別重復,因此遍歷所有樹是很重要的
也許這個答案有助於入門: 如何在boost :: property_tree中迭代XML結構
但是要非常小心迭代正在修改的樹。 您需要仔細檢查文檔中的迭代器失效規則以進行erase
如果有人需要它,我就是這樣做的(對不起,沒有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);
}
}
注意,如果要刪除多個分支,則在每個分支刪除后重復一個樹。 在大樹的情況下,這可能是一個問題。
靈感來自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; } }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.