【问题标题】:Boost Property Tree: Remove attribute from a nodeBoost Property Tree:从节点中删除属性
【发布时间】:2013-01-24 16:43:19
【问题描述】:

我有以下 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<gexf>
  <graph>
    <nodes>
      <node id="0" label="0" start="0" end="25"/>
      <node id="1" label="1" start="1"/>
      <node id="2" label="2" start="2"/>
      ...
    </nodes>
    <edges>
      <edge id="0" source="0" target="1" start="7" end="19"/>
      <edge id="1" source="0" target="2" start="8" end="20"/>
      ...
    </edges>
  </graph>
</gexf>

我想从带有source="0"target="1" 的边缘删除startend 属性。

我尝试这样做的方法是在以下代码中。假设 XML 文件名为 ptree_test.gexf,我将其读入,在树中找到正确的边,然后尝试使用 erase 去除属性。

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>

using boost::property_tree::ptree;

int main(int argc, char *argv[]) {

  ptree pt;

  read_xml("ptree_test.gexf", pt);

  // Now find edge (0, 1) and delete the start and end attributes
  ptree edge;
  int id1, id2;
  id1 = 0;
  id2 = 1;

  for(auto &e : pt.get_child("gexf.graph.edges")) {
    int s, t;
    s = e.second.get<int>("<xmlattr>.source");
    t = e.second.get<int>("<xmlattr>.target");

    // Check if this is the correct edge
    // Two checks because it can be reversed
    if((id1 == s && id2 == t) || (id1 == t && id2 == s)) {
      edge = e.second;
      break;
    }
  }

  for(auto & attr : edge.get_child("<xmlattr>")) {
    if(attr.first == "end" || attr.first == "start") {
      edge.erase(attr.first);
    }
  }

  write_xml(std::cout, pt);
  return 0;
}

这不起作用。它不会删除该属性。事实上,如果我输入一个打印返回 edge.erase(attr.first) 的调试语句,它会显示 0

【问题讨论】:

  • Boost.PropertyTree 不是一个快速而简单的 XML 解析器。不要像现在这样对待它。 XML 序列化程序用于存储和取消存储数据。它并不意味着以任意方式加载或修改 XML。简而言之,如果您需要操作 GEFX XML 文件,use a real XML parser.
  • @NicolBolas 我实际上从未阅读过该文件。我只是这样写了这个例子,因为它使它更短更容易理解。在我的库中,我在内存中编写 XML 文件,然后在完成后将其写出。在此过程中,可能会出现我需要以指定方式更改它的情况。我正在使用 boost 属性树,因为 boost 已经是我的库的依赖项。而不是引入另一个依赖项,我想我会坚持使用它来处理 XML 的东西。如果这是不可能的,我会使用别的东西。你是说这是不可能的吗?

标签: c++ c++11 boost boost-propertytree


【解决方案1】:

在回答之前,我想再次劝阻您不要将 Boost.PropertyTree 用作快速而肮脏的 XML 处理系统。请use a real XML parser;有很多可供选择,有些非常有效,需要很少的依赖维护。

不管怎样,你的问题来自your use of erase。您正在尝试从列表中删除一个元素,您正在迭代。那是行不通的。并非没有为您的循环进行特殊编码。

所以你不能在这里使用基于范围的 for 循环。您必须在迭代器上使用真正的 for 循环。

auto &children = edge.get_child();
for(auto &attrIt = children.begin(); attrIt != children.end();)
{
  auto &attr = *attrIt;
  //Do stuff here.


  if(attr.first == "end" || attr.first == "start")
    attrIt = children.erase(attrIt);
  else
    ++attrIt;
}

【讨论】:

  • 我想在更多地考虑这个问题并看到你的答案之后,我会去use a real XML parser。但是查看 boost 属性树的文档,其中两个签名采用迭代器,但一个采用密钥:size_type erase(const key_type &amp;) 所以我不确定为什么这不起作用 - 更不用说我的示例不会编译 if它需要一个迭代器。
  • 对,一些容器(通常是关联容器)确实支持直接擦除键。
  • @SeanLynch:你是对的;您的问题是从您正在迭代的列表中删除。
  • 我(老实说)很好奇为什么您认为不鼓励使用 Boost.PropertyTree 作为 XML 阅读器。 BPT 相对于一般的 XML 文件是效率问题还是不完整问题?
  • @alfC:您链接到一个页面,该页面准确解释了为什么不应该使用 BPT 来读取任意 XML。它非常明确地说,“XML 存储编码不能完美地往返。” BPT 的 XML 解析旨在读取 由 BPT 编写的 XML。至于“C++/STL 容器语法的优点”,PugiXML 提供了对元素和属性的迭代器访问。
【解决方案2】:

主要问题是你在这一行复制了子树:

  edge = e.second;

然后修改那个 copy 而不是原来的。后来,正如@NicolBolas 所说,您需要一个erase 的交互器。完整代码如下所示:

int main(){
        boost::property_tree::ptree pt;
        read_xml("ptree_test.gexf", pt, boost::property_tree::xml_parser::trim_whitespace);
        int id1, id2;
        id1 = 0;
        id2 = 1;
        for(auto &e : pt.get_child("gexf.graph.edges")) {
            int s, t;
            s = e.second.get<int>("<xmlattr>.source");
            t = e.second.get<int>("<xmlattr>.target");
            // Check if this is the correct edge
            // Two checks because it can be reversed
            if((id1 == s && id2 == t) || (id1 == t && id2 == s)){
                auto &children = e.second.get_child("<xmlattr>");
                for(auto attrIt = children.begin(); attrIt != children.end(); ++attrIt){
                  if(attrIt->first == "end" || attrIt->first == "start")
                    attrIt = children.erase(attrIt);
                }
                break; // maybe shouldn't be here to keep looking?
            }
        }
        write_xml("ptree_test_copy.gexf", pt, std::locale(), bpt::xml_writer_settings<std::string>{'\t', 1});
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 2016-11-13
    • 2018-05-08
    相关资源
    最近更新 更多