【问题标题】:Selecting a child text node amongst white space text nodes, in a complex XML element using XPath在使用 XPath 的复杂 XML 元素中,在空白文本节点中选择子文本节点
【发布时间】:2010-08-09 18:33:29
【问题描述】:

我一直在为此绞尽脑汁,但似乎无法正确解决,而且我在 Google 上没有找到正确的关键字..

我最近开始使用 XSLT 和 XPath 来为我的一个项目创建自然语言词汇表的 XML 描述。

问题是我选择对某些单词使用“混合内容”复杂元素,并且在某些情况下只想获取文本节点。

这是 XML 文档的一部分:

...
<entry category="substantiv">
  <word lang="sv">semester</word>
  <word lang="de">
    <article>der</article>Urlaub
    <plural>Urlaube</plural>
  </word>
</entry>
...

我的文档中有很多入口元素,在这种情况下,我想使用 /entry/word[@lang='de']/text() 获取“Urlaub”,因为我的换行符不起作用。我发现实际上有三个文本节点.. .../text()[2] 当然可以工作.. 但是,我事先不知道哪里会有换行符,或者有多少。如果 xml 格式如下,我的第一个版本的路径将起作用,但不是第二个:

...
<word lang="de"><article>der</article>Urlaub
  <plural>Urlaube</plural>
</word>
...

我想我想做的是选择 word[@lang='de'] 的所有直接文本节点,然后使用normalize-space() 删除不必要的空格。但是,我如何使用 XPath 做到这一点?或者,还有更好的方法?这似乎很容易,但我无法弄清楚。顺便说一下,我正在尝试在 XSLT 文档中执行此操作。

normalize-space(/entry/word[@lang='de']/text()[*]) 是我尝试过的事情之一,但这似乎做了别的事情。

/感谢任何帮助。

更新:

根据要求,这是 XSLT 的一部分:

...
<xsl:choose>
    <xsl:when test="@category='substantiv'">
        <em><xsl:value-of select="word[@lang='de']/article" /></em>
        <xsl:value-of select="normalize-space(word[@lang='de']/text()[2])" />
        <em>pl. <xsl:value-of select="word[@lang='de']/plural" /></em>
    </xsl:when>
...

此代码适用于第一个版本的格式化。澄清一下,我想要做的是在复杂元素&lt;word lang="de"&gt; 中获取文本节点的值,尽管它可能是用换行符和空格格式化的。我将如何处理该值取决于上下文,但现在我只是将它放在一个 xhtml 文档中。

更新 2: 我现在使用&lt;xsl:strip-space elements="*"/&gt;,它消除了文本节点为空的问题。我也在使用:

...
<xsl:choose>
  <xsl:when test="@category='substantiv'">
    <em><xsl:value-of select="word[@lang='de']/article" /></em>
    <xsl:text> </xsl:text>
    <xsl:value-of select="normalize-space(word[@lang='de']/text())" />
    <xsl:text>, </xsl:text>
    <em>pl. <xsl:value-of select="word[@lang='de']/plural" /></em>
  </xsl:when>
...

仍然需要规范化,因为在 XML 中的“Urlaub”之后仍然添加了一个空格。

当我需要访问我使用的 XSLT 文档之外的文本节点“Urlaub”时:
&lt;xsl:value-of select="normalize-space(word[@lang='de']/text()[normalize-space() != ''])" /&gt;

感谢所有帮助的人!

更新 3: 试图改进标题

【问题讨论】:

  • 如果没有相关的 XSLT 上下文,就无法自信地回答这个问题。请在您的问题中添加您正在努力解决的 XSLT 代码,以及您的实际意图(即想要的输出)。
  • @Tomalak,我不是在为 XSLT 苦苦挣扎,而是在 XPath 部分苦苦挣扎。但我添加了它以防万一它可能会提供一些必要的信息。
  • 您没有在 XSLT 上苦苦挣扎,这不是重点。 ;) 显示您的代码很有帮助,因为 XSLT 和 XPath 相互关联非常密切,并且许多问题的解决方案可能与您当前尝试的解决方案不同。 -- 查看我更改后的答案。
  • 是的,你完全正确。 XML 开启了一个全新的美妙世界,但它有时会让人非常困惑.. :)

标签: xml xslt xpath


【解决方案1】:

这种转变:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:value-of select="/*/entry/word[@lang='de']/text()[1]"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时(包装在 dict 顶部元素中):

<dict>
    <entry category="substantiv">
        <word lang="sv">semester</word>
        <word lang="de">
            <article>der</article>Urlaub
            <plural>Urlaube</plural>
        </word>
    </entry>
</dict>

准确地产生想要的结果

Urlaub

注意:使用&lt;xsl:strip-space&gt; 指令从源 XML 文档中消除所有仅包含空格的文本节点。

因此,不需要额外的处理(normalize-space() 等)

【讨论】:

  • 这是一个非常好的解决方案。维伦丹克! :)
  • 原来“Urlaub”之后还有空白,但这不是问题。
  • 删除了空白节点,即仅包含空白字符的文本节点。 “Urlaub”文本节点包含非空白字符,因此不受 的影响。
  • @Ags1,是的,您的评论是否指出此答案有问题?
  • 答案没有错,我只是向@nimbus77解释为什么Urlaub之后还有空间。
【解决方案2】:

试试:

/entry/word[@lang='de']/child::text()[normalize-space(.) != '']

意思是,抓取所有子文本节点,但不抓取那些标准化为空字符串的子节点。

-奥辛

【讨论】:

  • 提及child:: 轴是多余的。另外,normalize-space() 默认在当前节点上运行,所以虽然. 没有必要提及它。
  • 输入 word[@lang='de']/text()[normalize-space() != ''] 就可以了。谢谢!
【解决方案3】:

我认为这是您想要的框架,减去任何 normalize-space() 以使事物看起来完全符合您的要求。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <xsl:apply-templates select=".//word"/>
  </xsl:template>
  <xsl:template match="word">
    <xsl:apply-templates select=".//text()"/>
  </xsl:template>
  <xsl:template match="text()"><xsl:value-of select="."/><xsl:text> </xsl:text></xsl:template>  
</xsl:stylesheet>

键是.//text(),它返回上下文node()下方任何嵌套级别的所有子文本节点的串联。

【讨论】:

  • 这就是我认为.//text() 会做的事情。也许我做错了?如果我使用&lt;xsl:value-of select="normalize-space(word[@lang='de']//text())" /&gt;(还没有开始使用模板,但会使用),我什么也得不到。但如果我在我的 XPath 评估器中对其进行测试,它会发现 5 个可能的文本节点,因为还添加了“der”和“Urlaube”。
  • “关键是the .//text(),它返回所有子文本节点的连接” - 实际上,这是错误的。 //text() 选择所有文本节点,它返回一个单独节点的节点集,而不是连接的字符串。
【解决方案4】:

现在我看到了你的代码,我推荐这个:

<xsl:choose>
  <xsl:when test="@category='substantiv'">
    <em><xsl:value-of select="word[@lang='de']/article" /></em>^
    <!-- select the first non-empty text node and normalize it -->
    <xsl:value-of select="normalize-space(word[@lang='de']/text()[normalize-space() != ''][1])" />
    <em>pl. <xsl:value-of select="word[@lang='de']/plural" /></em>
  </xsl:when>

答案的原版

让您开始:

<entry category="substantiv">
  <word lang="sv">semester</word>
  <word lang="de">
    <article>der</article>Urlaub
    <plural>Urlaube</plural>
  </word>
</entry>

通过此 XSLT 1.0 时:

<!-- identity template copies everything 1:1, unless other templates apply -->
<xsl:template match="*|@*">
  <xsl:copy>
    <xsl:apply-templates select="*|@*" />
  </xsl:copy>
</xsl:template>

<!-- empty template: ignore every white-space-only text-node child of <word> -->
<xsl:template match="word/text()[normalize-space() = '']" />

会产生这个:

<entry category="substantiv">
  <word lang="sv">semester</word>
  <word lang="de"><article>der</article>Urlaub<plural>Urlaube</plural></word>
</entry>

这个答案是一个猜测,可能不是你所追求的。无论如何,您的问题都需要澄清。你认为你想要的和你实际上想要的并不总是一样。

【讨论】:

  • 啊,是的,我一点都不清楚。我不想改变格式化,只处理不同的格式化场景。但是你帮了我一些别的事情,所以你的回答仍然有用。谢谢! :)
  • @nimbus:你有没有注意到我答案的顶部发生了变化?
  • 是的,我做到了,这个改变就行了。谢谢你帮忙。我现在有点困惑 text() 应该如何工作,但如果我无法弄清楚,明天我会提出一个新问题。
  • @nimbus: text() 尽管有括号,但它不是函数。至少不是你想象的那样。它选择文本节点,就像foo 选择&lt;foo&gt; 元素一样。括号是一种将其与text 分开的方法,后者将选择&lt;text&gt; 元素。
  • 是的,我被它愚弄了。我今天还发现它被称为节点测试。我还认为它会自动将文本节点连接到一个字符串中,就像我以:word[@lang="de"] 结束 XPath 一样。但是,现在我知道得更好了。 :)
猜你喜欢
  • 1970-01-01
  • 2018-03-15
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 2018-07-20
  • 1970-01-01
相关资源
最近更新 更多