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

How to modify node element with boost::property_tree and 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, 0, 0, "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 没有 return 值。

(std::string)it2.second.data() 中的 C 风格强制转换非常危险,因为它有 reinterpret_cast-ing 不相关类型的风险。这种生硬的铸造没有任何理由。只需删除演员!

此外,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);

This would be correct, assuming that the attribute always exists and instead of ptModel you pass ptModel.get_child("sdf") to modifyModel).

其他注意事项:简化!

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

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

应该是这样的

  ptree const& pt2 = it.second;

  • 只用get_child_optional重复get_child更浪费
  • 好的做法是将 output/query 和突变分开。所以不要从 modelModifier
  • 内部调用 ptreeIterator
  • 另外,给函数起一个很好的描述性名称(这样你就不会不好意思地解释“函数 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, 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, double, double, std::string object_name, 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);
    }
}