Question

Suppose I have the following tree:

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");

which has the following serialized INFO form:

1
{
    2
    {
        3 value-1
        {
            4 nested-value
        }
        3 value-2
        33 other-value
    }
}

Is there a method to remove all nodes having a provided (possibly nested) path? I.e.:

remove(tree, "1.2.3");
BOOST_ASSERT(!tree.get_optional<std::string>("1.2.3") && 
             !tree.get_child_optional("1.2.3"));

with the result INFO form:

1
{
    2
    {
        33 other-value
    }
}

Looking at ptree docs and source code I've found several methods to remove immediate children of a tree (nested children are not accounted). Also, there are several methods to get a subtree by it's full path (even if it is nested). But since there is no way to easily get node's parent, I could not combine all these to get what I need.

Is there any easy way to get what I need, possibly w/o the need to reimplement tree traversal?

Was it helpful?

Solution

I don't think it can be done. It would be kind-a ok in JSON, but with INFO subtree keys can be repeated at each level, making it important to traverse all of the tree

Perhaps this answer helps to get started: How to iterate over XML structure in boost::property_tree

Be very careful about iterating a tree that's being modified, though. You will want to double check the iterator invalidation rules in the documentation for erase

OTHER TIPS

In case someone needs it, here is how I done it (sorry, no 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);
   }
}

Note, that in case there are several branches to delete, a tree is reiterated after every branch deletion. It could be a problem in case of a large tree.

Inspired by Alex Che's answer, here is my solution. It assumes no repeated keys, and no keys containing both data and child nodes. It returns a bool indicating success. It also optionally removes parent keys that are empty, instead of leaving an empty value for the key.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top