如何合并/更新boost::property_tree::ptree?

17

我已经阅读了boost::property_tree文档,但没有找到更新或合并ptree的方法。我该怎么做?

在下面的代码中,如何编写update_ptree函数?

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

我考虑过遍历新的ptree并使用“put”插入值。但是,“put”需要一个类型,我不知道如何从新的ptree中获取该信息并将其用作旧ptree的参数。

在update_ptree函数中,我尝试过使用以下方法:

pt_.add_child(".",pt);

我试图将pt添加为pt_根的子级,但不幸的是似乎无法实现。

有什么想法吗?

非常感谢任何帮助。

谢谢。

(我尝试将tags property_tree和ptree添加到这个问题中,但被禁止了)

2个回答

16

我认为你需要递归遍历property_tree。

你可以定义一个函数,递归地迭代每个节点,并为每个节点调用一个方法:

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

我们可以定义一个更简单的函数来调用之前的函数:
template<typename T>
void traverse(const boost::property_tree::ptree &parent, T &method)
{
  traverse_recursive(parent, "", parent, method);
}

现在,您可以修改类A以添加一个方法来合并一个节点并填充update_ptree方法:
#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());
  }    
}; 

唯一的限制是可能存在多个具有相同路径的节点。所有这些节点都将被使用,但只有最后一个节点会被合并。

谢谢。这是一个有趣的解决方案。我会编译并查看!但是你说的“可能有多个具有相同路径的节点”是什么意思?Tree_1=“a.b.c”=0更新树,Tree_2=“a.b.c”=1,“a.b.d”=2。只有“a.b.d”=2会被更新吗?(我会进行调试并查看) - mantler
有可能存在多个具有完全相同路径的节点。当Tree_1包含"a.b.c"=1,"a.b.c"=2和Tree_2包含"a.b.c"=1时,在使用Tree_1更新Tree_2后,Tree_2将包含"a.b.c"=2。 - J. Calleja
这是一段非常棒的代码。你怎么知道这个操作符可以用于 path_type 类型呢?:ptree::path_type curPath = childPath / ptree::path_type(it->first); 我在文档中没有看到这个操作符的定义。 - 2NinerRomeo
@2NinerRomeo 谢谢。我尝试使用该运算符,因为它是我在boost::filesystem中使用的运算符之一。另一方面,您可以在头文件文档中看到它。 - J. Calleja

7
Boost.Property tree目前不支持此功能: boost.org/doc/libs/1_48_0/doc/html/property_tree/appendices.html。请查看未来工作部分。
数学关系:ptree差异、并集、交集。
更新仅是差异后跟并集:a = (a - b) + b
一般解决方案需要递归遍历更新ptree并放置每个叶子节点。
然而,可以使用put_child构建足够好的解决方案。这可能能够满足您的所有需求,而无需复杂的通用解决方案。
void merge( ptree& pt, const ptree& updates )
{
   BOOST_FOREACH( auto& update, updates )
   {
      pt.put_child( update.first, update.second );
   }
}

好的解决方案有两个限制,恰巧它们与ini_parser具有相同的限制。

  • 树只能有两层(例如,“first.number”,但不是“first.again.number”)
  • 值只能存储在叶节点中。

谢谢。我会尝试这个方法,看看它的效果如何。你提到“树只能有两层”,真是让人感到有趣,我之前没有考虑过这一点。所以,对于任意深度的树,可能不存在解决办法或者“通用”的算法来完成我想做的事情,是吗? - mantler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接