问题描述
我对 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
以可靠的方式转储它。
列表
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);
}
}