How do I merge/update a boost::property_tree::ptree?
Asked Answered
X

2

17

I have read through the documentation for boost::property_tree and have not found a way to update or merge a ptree with another ptree. How do I do this?

Given the code below, how would the update_ptree function look like?

#include <iostream>
#include <boost/property_tree/ptree.hpp>
using boost::property_tree::ptree;

class A
{
  ptree pt_;
public:
  void set_ptree(const ptree &pt)
  {
    pt_ = pt;
  };
  void update_ptree(const ptree &pt)
  {
    //How do I merge/update a ptree?
  };
  ptree get_ptree()
  {
    return pt_;
  };
};

int main()
{
  A a;
  ptree pt;
  pt.put<int>("first.number",0);
  pt.put<int>("second.number",1);
  pt.put<int>("third.number",2);
  a.set_ptree(pt);
  ptree pta = a.get_ptree();

  //prints "0 1 2"
  std::cout << pta.get<int>("first.number") << " "
            << pta.get<int>("second.number") << " "
            << pta.get<int>("third.number") << "\n";


  ptree updates;
  updates.put<int>("first.number",7);
  a.update_ptree(updates);
  pta = a.get_ptree();

  //Because the update_tree function doesn't do anything it just prints "0 1 2".
  //I would like to see "7 1 2"
  std::cout << pta.get<int>("first.number") << " " 
            << pta.get<int>("second.number") << " " 
            << pta.get<int>("third.number") << "\n";

  return 0;
}

I have thought about iterating over the new ptree and using "put" to insert values. But "put" requires a type and I don't know how to get that information from the new ptree and use it as a argument for the old ptree.

One thing I have tried in the update_ptree function is using:

pt_.add_child(".",pt);

Basically I try to add the pt as a child to the root of pt_. Unfortunately this does not seem to work.

Any ideas?

I am grateful for any help.

Thank you.

(I tried to add the tags property_tree and ptree to this question but I wasn't allowed to)

Xanthic answered 16/11, 2011 at 15:26 Comment(0)
A
16

I think you have to recursively traverse the property_tree.

You can define a function that iterates on each node recursively and calls a method for each node:

template<typename T>
void traverse_recursive(const boost::property_tree::ptree &parent, const boost::property_tree::ptree::path_type &childPath, const boost::property_tree::ptree &child, T &method)
{
  using boost::property_tree::ptree;

  method(parent, childPath, child);
  for(ptree::const_iterator it=child.begin();it!=child.end();++it) {
    ptree::path_type curPath = childPath / ptree::path_type(it->first);
    traverse_recursive(parent, curPath, it->second, method);
  }
}

We can define a simpler function in order to call the previous one:

template<typename T>
void traverse(const boost::property_tree::ptree &parent, T &method)
{
  traverse_recursive(parent, "", parent, method);
}

Now, you can modify the class A in order to add one method to merge just one node and fill the update_ptree method:

#include <boost/bind.hpp>

class A {  
  ptree pt_; 

public:   
  void set_ptree(const ptree &pt)   {    
    pt_ = pt; 
  }

  void update_ptree(const ptree &pt)   {  
    using namespace boost;
    traverse(pt, bind(&A::merge, this, _1, _2, _3));
  }

  ptree get_ptree()   { 
    return pt_;  
  }

protected:
  void merge(const ptree &parent, const ptree::path_type &childPath, const ptree &child) {
    pt_.put(childPath, child.data());
  }    
}; 

The only limitation is that it is possible to have several nodes with the same path. Every one of them would be used, but only the last one will be merged.

Anastigmatic answered 17/11, 2011 at 23:26 Comment(4)
Thanks. This is an interesting solution. Gonna compile an see! But what do you mean by "it is possible to have several nodes with the same path"? Tree_1= "a.b.c"=0 update tree Tree_2="a.b.c"=1, "a.b.d"=2. Would only "a.b.d"=2 be updated? (Gonna debug and see)Xanthic
It is possible to have more than one node with exactly the same path. When Tree_1 contains "a.b.c"=1, "a.b.c"=2 and Tree_2 contains "a.b.c"=1, after updating Tree_2 with Tree_1, Tree_2 would contain "a.b.c"=2.Anastigmatic
This is a really cool piece of code. How did you know that this operator would work on path_type?: ptree::path_type curPath = childPath / ptree::path_type(it->first); I can't see that it the opeator is defined in the documentation.Sulfate
@Sulfate Thank you. I tried that operator because it is the one I use with boost::filesystem. On the other hand, you can see it on the headers documentationAnastigmatic
S
7

Boost.Property tree does not support this, yet: boost.org/doc/libs/1_48_0/doc/html/property_tree/appendices.html. Look at the future work section.

Mathematical relations: ptree difference, union, intersection.

An update is simply a difference followed by a union. a = (a - b) + b.

The general solution would require recursively traversing update ptree and putting each leaf.

However a good enough solution can be built with put_child. This may do all you need, without the complexity of a general solution.

void merge( ptree& pt, const ptree& updates )
{
   BOOST_FOREACH( auto& update, updates )
   {
      pt.put_child( update.first, update.second );
   }
}

The good enough solution has two limitations, by coincidence they are the same limitations the ini_parser has.

  • the tree can only be two layers (e.g. "first.number", but not "first.again.number" )
  • values can only be stored in leaf nodes.
Shaky answered 16/11, 2011 at 22:3 Comment(1)
Thank you. I will try this and see how it works. It is interesting what you say about "the tree can only be two layers..", I had not thought about that. So it could be that there is no work around or "generic" algorithm to do what I want to do (for an arbitrary tree depth)?Xanthic

© 2022 - 2024 — McMap. All rights reserved.