在元素之间选择多个text()节点

时间:2014-01-28 10:42:50

标签: xml xslt xpath xslt-1.0

我需要将以下xml输出到浏览器,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<content>
  <text>
    <h1>Services</h1>
    <h2>Engineering</h2>
    This is the first bit of text:
      <listofitems>
        <listitem>Database Analysis Tools</listitem>
        <listitem>Website Development</listitem>
        <listitem>Intranet Development</listitem>
        <listitem>Database Development</listitem>
      </listofitems> 
    This is the second piece of text:
      <listofitems>
        <listitem>Hardware</listitem>
        <listitem>Software</listitem>
      </listofitems>
    This is the final piece of text.
  </text>
</content>

问题在于&lt; text&gt;中的文本字符串。 element - 未包含在自己标签中的位,即“这是文本的第一位:','这是第二段文字:'和'这是最后一段文字。'

如果我使用&lt; xsl:value-of select =“。”/&gt;,我可以在一个块中输出所有三位文本,但显然这不是它们在原始XML中的显示方式。如果我使用&lt; xsl:value-of select =“text()”/&gt;什么都没有回来(我相信text()只引用第一个子节点,在这种情况下是另一个元素)。

我可以用&lt; xsl:value-of select =“node()[last()] / self :: text()”&gt ;;输出最后一位文本。我可以通过直接引用它来访问每个text()节点,例如&lt; xsl:value-of select =“text()[3]”/&gt;,但因为这意味着要使用随机XML,它可以是任何格式,我将无法直接在此引用节点办法。我在text()[*]上尝试过for-each,但它没有用。我有什么想法可以做到这一点吗?

3 个答案:

答案 0 :(得分:1)

如果文本节点是text元素的子元素,则编写单独的模板以匹配文本节点:

<xsl:template match="text()[parent::text]">

这样,其间的任何其他子元素都不会干扰。然后,编写第二个模板,以防止XSLT处理器在listitem元素内输出文本:

<xsl:template match="*[parent::text]"/>

如果省略了上面的模板匹配,XSLT的默认行为将导致同时输出listitem子项的任何文本节点。

请注意,XSLT 1.0和2.0在处理文本节点方面存在差异。你的一种方法:

<xsl:value-of select="text()"/>

实际上是否在XSLT 2.0中输出所有文本节点。下面是一个适用于1.0和2.0的样式表。

<强>样式表

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="text"/>
   <xsl:strip-space elements="*"/>

   <xsl:template match="//text">
      <xsl:apply-templates/>
   </xsl:template>

   <xsl:template match="text()[parent::text]">
      <xsl:value-of select="."/>
   </xsl:template>

   <xsl:template match="*[parent::text]"/>

</xsl:stylesheet>

<强>输出

This is the first bit of text: This is the second piece of text: This is the final piece of text.

或者,如果您不需要处理text的子元素,请向select添加apply-templates属性:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:template match="//text">
      <xsl:apply-templates select="text()"/>
   </xsl:template>

   <xsl:template match="text()[parent::text]">
      <xsl:copy/>
   </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

当这个样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="content/text/text()">
        <span><xsl:value-of select="."/></span>
    </xsl:template>

</xsl:stylesheet>

适用于您的输入。它输出:

<?xml version="1.0" encoding="utf-8"?>
<content>
    <text>
        <h1>Services</h1>
        <h2>Engineering</h2>
        <span>This is the first bit of text:</span>
        <listofitems>
            <listitem>Database Analysis Tools</listitem>
            <listitem>Website Development</listitem>
            <listitem>Intranet Development</listitem>
            <listitem>Database Development</listitem>
        </listofitems>
        <span>This is the second piece of text:</span>
        <listofitems>
            <listitem>Hardware</listitem>
            <listitem>Software</listitem>
        </listofitems>
        <span>This is the final piece of text.</span>
    </text>
</content>

这会解决你的问题吗?

答案 2 :(得分:0)

经过多次摆弄后,我认为这已经足够接近了:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="*">
  <xsl:param name="indent"/>
    <xsl:if test="parent::* or preceding-sibling::*">
        <br/>
    </xsl:if>
      <xsl:value-of select="$indent" disable-output-escaping="yes"/>
      <!-- open angle brackets -->
      &lt;<xsl:value-of select="name()"/>
      <!-- output any attributes and values -->
      <xsl:for-each select="@*">
        <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
        <xsl:value-of select="name(.)"/>="<b><xsl:value-of select="."/></b>"
      </xsl:for-each>
      <!-- close angle brackets -->
      &gt;
      <!-- output text content -->
      <xsl:apply-templates>
        <xsl:with-param name="indent"><xsl:value-of select="$indent"/>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;</xsl:with-param>
      </xsl:apply-templates>
      <!-- close tag -->
      <xsl:if test="child::*">
        <br/><xsl:value-of select="$indent" disable-output-escaping="yes"/>
      </xsl:if>
      &lt;/<xsl:value-of select="name()"/>&gt;  
</xsl:template>

<xsl:template match="text()">
    <xsl:value-of select="."/>
</xsl:template>

</xsl:stylesheet>

这给出了这个:

<content> 
     <text> 
         <h1>Services</h1> 
         <h2>Engineering</h2> This is the first bit of text: 
         <listofitems> 
             <listitem>Database Analysis Tools</listitem> 
             <listitem>Website Development</listitem> 
             <listitem>Intranet Development</listitem> 
             <listitem>Database Development</listitem> 
        </listofitems> This is the second piece of text: 
         <listofitems> 
             <listitem>Hardware</listitem> 
             <listitem>Software</listitem> 
        </listofitems> This is the final piece of text. 
    </text> 
</content>

那些恼人的文本片段至少在大致正确的区域内,并且模板应该与任何旧的XML一起使用。

非常感谢你的帮助。