【问题标题】:XPath expression ignoring namespaces with predicates referencing child elementsXPath 表达式忽略带有引用子元素的谓词的命名空间
【发布时间】:2016-08-22 14:47:50
【问题描述】:

我正在从事一个迄今为止依赖于设置的项目: DocumentBuilderFactory.setNamespaceAware(false); 实现命名空间灵活的 xpath(忽略可能传入的任何前缀)。

这仅在过去有效,因为正在使用 xalan 转换器,虽然从技术上讲,将命名空间感知设置为 false 的行为充其量应该是未定义的,但对于给定的 xml,xalan 将以下 xpath 视为有效。

xml:

<t:GiftBasket xmlns:t="http://tastyTreats.com/giftbasket">
    <t:Fruit>
        <t:Apple>
            <t:Name>Cameo</t:Name>
            <t:Color>Red</t:Color>
            <t:TasteDescription>Juicy, crisp, and sweet with just a touch of tart, the Cameo is thought to come from both the Red and the Yellow Delicious.</t:TasteDescription>
        </t:Apple>
        <t:Apple>
            <t:Name>Empire</t:Name>
            <t:Color>Red</t:Color>
            <t:TasteDescription>The interior is crisp and creamy white while being firmer than the McIntosh, so it makes for a good cooking apple. </t:TasteDescription>
        </t:Apple>
        <t:Apple>
            <t:Name>Granny Smith</t:Name>
            <t:Color>Green</t:Color>
            <t:TasteDescription>Hard feel, crisp bite, and extremely tart taste. Some prefer to cook it, which sweetens it up.</t:TasteDescription>
        </t:Apple>
    </t:Fruit>
    <t:Candy>
        <t:Chocolate>Dark</t:Chocolate>
    </t:Candy>
</t:GiftBasket>

xpath:

/GiftBasket/Fruit/Apple[Color='Red'][contains(TasteDescription,'sweet')]

在切换到 xslt 2.0 时,我切换到了 Saxon-HE 变压器。撒克逊人是一个更精确的变压器(好东西 IMO)。它认识到这些 xpath 表达式是“错误的”,我现在必须修复一堆像上面这样的 xpath 表达式才能工作,而不管引用的命名空间前缀是什么(客户端可以选择将 URI http://tastyTreats.com/giftbasket 前缀为“fluffybunnies”,据我所知)

我收到了其他很好的建议 here 关于如何利用 local-name() 功能来实现我需要的大部分内容。我的 xpath 现在显示为:

/*[local-name()='GiftBasket']/*[local-name()='Fruit']/*[local-name()='Apple'][Color='Red'][contains(TasteDescription,'sweet')]

然而,这仍然不准确,因为谓词中引用的元素仍然引用确切的元素名称ColorTasteDescription,而没有命名空间灵活。有没有更好的方法来为所有口味描述中包含“甜”的红苹果编写 xpath,同时保持命名空间前缀的灵活性?

【问题讨论】:

  • 输入 XML 中使用的前缀对于您的 XPath 表达式根本不重要,重要的是使用的命名空间 URI。假设它是恒定的并且在 Saxon 中是已知的,您可以将其设置为 XPath 的默认命名空间,并且可以像以前一样使用 /GiftBasket/Fruit/Apple[Color='Red'][contains(TasteDescription,'sweet')]。您如何设置默认命名空间取决于您使用的 Java API,因此您需要告诉我们您是继续使用 JAXP API 还是已切换到 Saxon s9api。
  • 我通过调用 javax.xml.parsers.DocumentBuilderFactory.parse(is) 将文件中的 xml 读取到 InputSource 中,然后将其转换为 org.w3c.dom.Document。然后我通过调用 javax.xml.xpath.XPathFactory.newInstance().newXPath() 分别实例化一个 javax.xml.xpath.XPath。最后,我使用 xpath.evaluate(expression, document, javax.xml.xpath.XPathConstants.NODESET) 评估表达式并询问生成的 org.w3c.dom.NodeList.getLength() > 0。我没有看到对JAXP 或撒克逊 s9api。我怎样才能知道正在使用哪个?
  • 查看答案,我认为将那里显示的代码应用于您的 xpath 对象就足够了。
  • 我做了一个调试会话来验证,抱歉所有的问题。正在使用的具体 xpath 评估器类是:net.sf.saxon.xpath.XPathEvaluator 而该类引用类型为 et.sf.saxon.xpath.JAXPXPathStaticContext 的上下文,所以我假设你的答案是正确的。我会试一试,感谢大家的牵手。

标签: xpath xml-namespaces


【解决方案1】:

正如您提到的 XSLT 2.0,在 XSLT 2.0 中您可以定义

<xsl:stylesheet xpath-default-namespace="http://tastyTreats.com/giftbasket" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

然后使用不带前缀名称的 XPath 表达式和 XSLT 匹配模式来选择该特定命名空间中的元素,即路径 /GiftBasket/Fruit/Apple[Color='Red'][contains(TasteDescription,'sweet')] 应该可以正常工作以选择具有命名空间 http://tastyTreats.com/giftbasket 的输入文档中的元素。

如果您在 Saxon 9 中使用 JAXP XPath API,请参阅https://saxonica.plan.io/boards/3/topics/1649,了解如何使用该 API 设置此类默认命名空间:

((XPathEvaluator)xpath).getStaticContext().setDefaultElementNamespace("http://tastyTreats.com/giftbasket");

【讨论】:

  • 指定的正确答案。对 xpath 表达式特别重要的是最后一行代码,它允许我按照最初的意图使用谓词。 XSLT 参考对于还需要运行 xslt 的人来说非常方便。感谢所有的帮助和手握!这显着清理了 xpath 表达式备份:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-12
  • 2015-06-21
  • 2011-05-25
  • 1970-01-01
  • 2011-04-25
相关资源
最近更新 更多