9

我有一堆存储在 XML 文件中的分层数据。我正在使用 TinyXML 将其封装在手工制作的类后面。给定一个将源签名描述为一组(频率、级别)对的 XML 片段,有点像这样:

<source>
  <sig><freq>1000</freq><level>100</level><sig>
  <sig><freq>1200</freq><level>110</level><sig>
</source>

我正在用这个提取对:

std::vector< std::pair<double, double> > signature() const
{
    std::vector< std::pair<double, double> > sig;
    for (const TiXmlElement* sig_el = node()->FirstChildElement ("sig");
        sig_el;
        sig_el = sig_el->NextSiblingElement("sig"))
    {
        const double level = boost::lexical_cast<double> (sig_el->FirstChildElement("level")->GetText());
        const double freq =  boost::lexical_cast<double> (sig_el->FirstChildElement("freq")->GetText());
        sig.push_back (std::make_pair (freq, level));
    }
    return sig;
}

其中 node() 指向<source>节点。

问题:我会使用 XPath 库获得更整洁、更优雅、更易于维护或以任何其他方式更好的代码吗?

更新:我使用 TinyXPath 两种方式进行了尝试。它们都没有真正起作用,这显然对它们不利。我在做一些根本错误的事情吗?如果这就是 XPath 的样子,我认为它不会让我得到任何东西。

std::vector< std::pair<double, double> > signature2() const
{
    std::vector< std::pair<double, double> > sig;
    TinyXPath::xpath_processor source_proc (node(), "sig");
    const unsigned n_nodes = source_proc.u_compute_xpath_node_set();
    for (unsigned i = 0; i != n_nodes; ++i)
    {
        TiXmlNode* s = source_proc.XNp_get_xpath_node (i);
        const double level = TinyXPath::xpath_processor(s, "level/text()").d_compute_xpath();
        const double freq =  TinyXPath::xpath_processor(s, "freq/text()").d_compute_xpath();
        sig.push_back (std::make_pair (freq, level));
    }
    return sig;
}

std::vector< std::pair<double, double> > signature3() const
{
    std::vector< std::pair<double, double> > sig;
    int i = 1;
    while (TiXmlNode* s = TinyXPath::xpath_processor (node(), 
        ("sig[" + boost::lexical_cast<std::string>(i++) + "]/*").c_str()).
        XNp_get_xpath_node(0))
    {
        const double level = TinyXPath::xpath_processor(s, "level/text()").d_compute_xpath();
        const double freq =  TinyXPath::xpath_processor(s, "freq/text()").d_compute_xpath();
        sig.push_back (std::make_pair (freq, level));
    }
    return sig;
}

作为次要问题,如果是这样,我应该使用哪个 XPath 库?

4

4 回答 4

5

一般来说,我倾向于更喜欢基于 XPath 的解决方案,因为它们简洁且多功能,但老实说,在您的情况下,我认为使用 XPath 不会为您的signature.

原因如下:

代码优雅
你的代码既漂亮又紧凑,使用 XPath 表达式不会变得更好。

内存占用
除非您的输入 XML 配置文件很大(一种矛盾修饰法)并且 DOM 解析将需要大量内存占用,对此没有证据表明使用 XPath 将是决定性的治疗方法,否则我会坚持使用 DOM。

执行速度
在这样一个简单的 XML 树上,执行速度应该是相当的。如果存在差异,则可能是 TinyXml 的优势,因为freqlevel标签在给定节点下的搭配。

库和外部参考 这是决定性的一点。
C++ 世界中领先的 XPath 引擎是XQilla。它支持 XQuery(因此同时支持 XPath 1.0 和 2.0)并得到 Oracle 的支持,因为它是由负责 Berkeley DB 产品(包括使用 XQilla的 Berkeley DB XML )的小组开发的。
希望使用 XQilla 的 C++ 开发人员面临的问题是他们有多种选择

  1. 使用 Xerces 2 和 XQilla 2.1 会在你的代码中添加强制转换。
  2. 使用 XQilla 2.2+ 并使用 Xerces 3(这里不需要强制转换)
  3. 使用与 TinyXml 完美集成的TinyXPath,但存在许多限制(例如,不支持命名空间)
  4. 混合 Xerces 和 tinyXml

总之,在您的情况下,仅仅为了它而切换到 XPath,如果有的话,也不会带来什么好处。

然而,XPath 是当今开发人员工具箱中非常强大的工具,没有人可以忽视它。如果您只想练习一个简单的示例,那么您的示例与任何示例一样好。然后,我会记住以上几点,并且可能无论如何都会使用TinyXPath

于 2011-03-05T07:34:56.797 回答
3

如果您需要灵活地对提取的值进行运行时更改,则需要 XPath。

但是,如果您不太可能需要这种灵活性,或者重新编译以扩展您提取的内容不是问题,并且事情不会经常更改,或者用户不需要更新表达式。或者,如果您拥有的东西对您来说很好,您就不需要 XPath,而且有很多应用程序不使用它。

至于它是否更具可读性,是的,它确实可以。但是,如果您只是提取一些值,我会质疑是否需要引入另一个库。

我当然会更好地记录您目前拥有的内容,因为那些不熟悉 tinyxml 或 xml 库的人可能不确定它在做什么,但它并不难理解。

我不确定 XPath 增加了什么样的开销,但我怀疑它可能会增加一些。对于大多数人来说,我猜他们根本不会注意到任何差异,这可能不是您或大多数人关心的问题,但请注意它,以防您担心。

如果您确实想使用 xpath 库,那么我只能说我使用了Xerces-C++附带的库,而且学习起来并不难。我以前使用过 TinyXML,这里有人提到过 TinyXPath。我没有这方面的经验,但它是可用的。

在第一次学习 XPath 表达式时,我还发现这个链接很有用。 http://www.w3schools.com/xpath/default.asp

于 2011-03-05T07:55:26.753 回答
1

XPath 就是为此而设计的,因此如果您使用它,您的代码当然会“更好”。

我不能推荐一个特定的 c++ XPath 库,但即使在大多数情况下使用一个是正确的决定,在添加一个之前做一个成本/收益分析。也许是雅格尼

于 2011-03-04T14:31:41.523 回答
1

这个 XPath 表达式

/*/sig[$pN]/*

选择XML 文档顶部元素的第 $pN 个子元素的所有子元素(仅一对freq和)。levelsig

该字符串$pN应替换为特定的正整数,例如:

/*/sig[2]/*

选择这两个元素

<freq>1200</freq><level>110</level>

使用 XPath 表达式,因为这显然比提供的 C++ 代码更短且易于理解。

另一个优点是可以在C# 或 Java 或 ... 程序中使用相同的 XPath 表达式,而无需以任何方式对其进行修改——因此遵守 XPath 会带来非常高的可移植性。

于 2011-03-04T18:42:42.680 回答