如何使用 boost::property_tree 和 put_value(const Type &value) 修改节点元素

问题描述

我对 boost::property_tree 还很陌生,我在完成一项简单的任务时遇到了一些麻烦。

我有一个认的 xml 文件,要复制该文件并使其唯一,参数通过下面的 ptree & modelModifier(...) 函数传入。我想要做的就是将 xml 解析为 ptree,然后将 name 字段(以及其他字段,但让我们以 name 开头)从“认”修改为从object_name 变量,然后将其写回原始 ptree。

函数 pTreeIterator 只是遍历每个孩子并显示内容

xml

<?xml version='1.0'?>
<sdf version='1.7'>
  <model name='default'>
    <link name='link'>
      <inertial>
        <mass>3.14999</mass>
        <inertia>
          <ixx>2.86712</ixx>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyy>2.86712</iyy>
          <iyz>0</iyz>
          <izz>0.524998</izz>
        </inertia>
        <pose>0 0 0 0 -0 0</pose>
      </inertial>
    </link>
  </model>
</sdf>

代码

void ptreeIterator(ptree & pt)
{
    using boost::property_tree::ptree;
    for (auto&it : pt)
    {
        std::cout << __FUNCTION__ << std::endl;
        std::cout << "------------------------------------------------------" << std::endl;
        std::cout << "Iteration output: " << std::endl;
        std::cout << "1: pt1: " << it.first << std::endl;

        if(pt.get_child_optional(it.first))
        {
            cout << "pt.get_child_optional(it.first) ---> " << it.first << endl;
            ptree pt2 = pt.get_child(it.first);
            for (auto&it2 : pt2)
            {
                std::cout << "\t2: " << "pt2: " << it2.first << " :: " << (std::string)it2.second.data() << std::endl;
                if(pt2.get_child_optional(it2.first))
                {
                    ptree pt3 = pt2.get_child(it2.first);
                    for (auto&it3 : pt3)
                    {
                        std::cout << "\t\t3: " << "pt3: " << it3.first << " :: " << (std::string)it3.second.data() << std::endl;
                    }
                }
            }
        }
    }
}

ptree & modelModifier(ptree &model,double px,double py,std::string object_name,uint16_t height)
{
    for(auto &it:model){
        cout << "it.first = " << it.first << endl;
        if(it.first=="model"){
            cout << "MODEL TAG" << endl;
            model.put_value(object_name);
        }
        ptreeIterator(model);
    }

}

int main(){
    ptree ptModel;

    const string filenametoInsert = "model.sdf";
    
    std::ifstream ifsModel(filenametoInsert,std::ifstream::binary);

    read_xml(ifsModel,ptModel,boost::property_tree::xml_parser::trim_whitespace);         

    modelModifier(ptModel,"TEST1234567",30);
    
    return 0;
}

输出

it.first = model
it.second.data() 
ptreeIterator
------------------------------------------------------
Iteration output: 
1: pt1: model
pt.get_child_optional(it.first) ---> model
        2: pt2: <xmlattr> :: 
                3: pt3: name :: default

预期产出

it.first = model
it.second.data() 
ptreeIterator
------------------------------------------------------
Iteration output: 
1: pt1: model
pt.get_child_optional(it.first) ---> model
        2: pt2: <xmlattr> :: 
                3: pt3: name :: TEST1234567

解决方法

首先,您的代码有 UB,因为 modelModifier 不返回值。

(std::string)it2.second.data() 中的 C 样式转换极其危险,因为它冒着 reinterpret_cast 无关类型的风险。这种生硬的铸造是有原因的。只需移除演员表即可!

另外,ptreeIterator 应该是 ptree const&,而不是 ptree&

修复这些后,示例不会显示您声明的输出,而是显示 (Live On Coliru)

it.first = sdf
ptreeIterator
------------------------------------------------------
Iteration output:
1: pt1: sdf
pt.get_child_optional(it.first) ---> sdf
        2: pt2: <xmlattr> ::
                3: pt3: version :: 1.7
        2: pt2: model ::
                3: pt3: <xmlattr> ::
                3: pt3: link ::

现在,即使在您的问题输出中,您也可以清楚地看到 model 节点与其名称属性之间的区别,这显然是您想要修改的。只需编写代码即可访问:

it.second.get_child("<xmlattr>.name").put_value(object_name);

这是正确的,假设该属性始终存在并且您将 ptModel 传递给 ptModel.get_child("sdf") 而不是 modifyModel)。

其他注意事项:简化!

也就是说,请简化整个事情!

  ptree pt2 = pt.get_child(it.first);

应该是这样的

  ptree const& pt2 = it.second;

  • 使用 get_child_optional 只与 get_child 重复更浪费
  • 好的做法是将输出/查询和变异分开。所以不要从内部调用 ptreeIterator modelModifier
  • 另外,给函数一个好的描述性名称(这样你就不会害羞地解释“函数 pTreeIterator 只是遍历每个孩子并显示其内容” - 只需将其命名为 displayModel?)
  • 与其煞费苦心(且有缺陷)迭代特定模型并以非常令人困惑的定制方式打印它,只需使用 write_xml/write_info/write_json 以可靠的方式转储它。

列表

Live On Coliru

namespace Model {
    void display(ptree const& pt)
    {
        write_json(std::cout << __FUNCTION__ << "\n---------------\n",pt);
    }

    void modify(ptree& model,double,std::string object_name,uint16_t)
    {
        for (auto& it : model) {
            std::cout << "root = " << it.first << std::endl;
            it.second.get_child("model.<xmlattr>.name").put_value(object_name);
        }
    }
}

印刷品

root = sdf
display
---------------
{
    "sdf": {
        "<xmlattr>": {
            "version": "1.7"
        },"model": {
            "<xmlattr>": {
                "name": "TEST1234567"
            },"link": {
                "<xmlattr>": {
                    "name": "link"
                },"inertial": {
                    "mass": "3.14999","inertia": {
                        "ixx": "2.86712","ixy": "0","ixz": "0","iyy": "2.86712","iyz": "0","izz": "0.524998"
                    },"pose": "0 0 0 0 -0 0"
                }
            }
        }
    }
}

奖金

在 name 属性可能不存在的情况下,以下代码将动态创建它:

void modify(ptree& model,uint16_t)
{
    ptree value;
    value.put_value(object_name);
    for (auto& it : model) {
        std::cout << "root = " << it.first << std::endl;
        it.second.put_child("model.<xmlattr>.name",value);
    }
}