【问题标题】:XPath 1.0: Select all child nodes that have a given parent, selecting empty if the parent does not have such a child node?XPath 1.0:选择所有具有给定父节点的子节点,如果父节点没有这样的子节点,则选择空?
【发布时间】:2013-02-05 14:42:00
【问题描述】:

使用 XPath 1.0 和下面的 XML,我如何选择所有具有“b”父节点的“c”节点,如果“b”父节点没有“c”节点,则选择“”,即获取@987654321 @ 作为结果节点集(按文档顺序)?

我正在使用 VTD-XML 2.11 (Java),我正在寻找一个 XPath 评估,它将返回上述结果而无需循环。

<a>
  <b>
    <c>c1</c>
  </b>
  <b/>
  <b>
    <c>c3</c>
  </b>
</a>

我浏览了问题并找到了一些有趣的指针,例如 Oliver Becker 的方法,但到目前为止我还没有找到解决方案。

任何帮助将不胜感激。

谢谢。

【问题讨论】:

  • 您希望 xpath 返回节点集还是字符串作为结果?
  • 我正在寻找 xpath 评估以返回由 {"c1", "", "c3"} 字符串组成的节点集。
  • 节点的顺序重要吗?
  • 是的,(文件)顺序很重要。
  • 出于性能原因,vtd-xml 不保证文档顺序,除非评估过程自然而然地导致它......将不得不考虑这一点,看看我是否能想出一些答案你

标签: xpath vtd-xml


【解决方案1】:

您使用什么来评估这些 XPath?我想不出一个很好的方法,但是根据您使用的方法,选择所有 bs 可能很容易,遍历它们,并为每个使用 c 的值或空白值。

这不是一个很好的方法,但它适用于您的示例输入:

//b/c | //b[not(*)] | //b[not(c) and *]/text()

解释是:

  • 选择b下的所有cs
  • 如果b 元素没有子元素,则选择它们自己
  • 选择任何没有c 子级的bs 的第一个文本节点。

假设任何带有子元素的b 至少有一个文本节点并且其中第一个完全是空白,这将起作用。

使用 XSLT 进行验证:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <root>
      <xsl:for-each select="//b/c | //b[not(*)] | //b[not(c) and *]/text()[1]">
        <item>
          <xsl:value-of select="."/>
        </item>
      </xsl:for-each>
    </root>
  </xsl:template>
</xsl:stylesheet>

在示例输入上运行时的输出:

<root>
  <item>c1</item>
  <item></item>
  <item>c3</item>
</root>

【讨论】:

  • 不幸的是,当我使用 VTD-XML 2.11 (Java) 运行它时,它会返回 "c1", "c3", ""
  • 我明白了。我认为这不是 XPath 应该产生的结果(它应该按文档顺序选择节点),但我想不出比这更好的了。您是否有不想使用循环的原因? VTD-XML 有没有办法做到这一点?
  • 不确定为什么 VTD-XML 在这种情况下不按文档顺序选择节点,好问题。基本上,我希望使用通用循环来评估多个 XPath 表达式,每个表达式都会产生一个结果列表,因此不能根据表达式使用自定义解析逻辑。
【解决方案2】:

这样的?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.IO;

namespace Xpath
{
    class Program
    {
        static void Main(string[] args)
        {

            string xml = @"<a>
              <b>
                <c>c1</c>
              </b>
              <b/>
              <b>
                <c>c3</c>
              </b>
              <b>x</b>
            </a>";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            string xpath = "//b/c/text()|//b[count(child::*) = 0]|//b[count(child::*) = 0]/text()";

            StringReader stringReader = new StringReader(xml);
            XmlReader xmlReader = XmlReader.Create(stringReader);

            XPathDocument xpathDoc = new XPathDocument(xmlReader);

            XPathNavigator xpathNav = xpathDoc.CreateNavigator();
            XPathNodeIterator xpathIter = xpathNav.Select(xpath);

            foreach (XPathNavigator navigator in xpathIter)
            {
                if (navigator.ValueType == typeof(string))
                {
                    Console.WriteLine("'" + navigator.Value + "'");
                }
            }
        }
    }
}

输出是:

'c1'
''
'c3'
'x'

【讨论】:

  • 当我运行它(使用我的原始 XML)时,这将返回 "c1", "c3", "", "",不幸的是,使用 VTD-XML 2.11 (Java)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
相关资源
最近更新 更多