Delimiting the output for xslt (in non trivial cas

2019-07-27 13:33发布

I would like to know the best way to delimit xml output in an xsl transform. "position() != last()" doesn't always work because it makes assumptions about the input.

This toy example demonstrates:

  <xsl:template match="/">
    <html>
      <body>
        <xsl:for-each select="//cd">
          <xsl:if test="title !='Unchain my heart'">
            <xsl:value-of select="title" />
            <xsl:if test="position() != last()">
              <xsl:text>; </xsl:text>  <!-- sometimes the delimiter will end if last element is a duplicate; -->
            </xsl:if>
          </xsl:if>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

will result in

... Pavarotti Gala Concert; The dock of the bay; Picture book; Red; 

note the trailing semicolon.

Any ideas?

(xml data from this example http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog)

3条回答
甜甜的少女心
2楼-- · 2019-07-27 14:05

Change it to:

    <xsl:for-each select="//cd[title !='Unchain my heart']">
        <xsl:value-of select="title" />
        <xsl:if test="position() != last()">
          <xsl:text>; </xsl:text>
        </xsl:if>
    </xsl:for-each>

or in XSLT 2.0,

<xsl:value-of select="//cd/title[. !='Unchain my heart']" separator="; "/>
查看更多
Fickle 薄情
3楼-- · 2019-07-27 14:23

One thing you can do is use the following:: axis to see if you're in the last title.

Example:

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

    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates select="catalog/cd/title[. != 'Unchain my heart']"/>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="title">
        <xsl:value-of select="."/>
        <xsl:if test="following::cd/title[. != 'Unchain my heart']">
            <xsl:text>; </xsl:text>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

You can still use that in your xsl:for-each if you want to keep it.

Also, if you're using XSLT 2.0 you can use the separator attribute on xsl:value-of.

Example:

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

    <xsl:template match="/">
        <html>
            <body>
                <xsl:value-of select="catalog/cd/title[. != 'Unchain my heart']" separator="; "/>
            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>

Also, if you're trying to drop the "Unchain my heart" title, try putting it in a predicate instead of the xsl:if (it's not wrong, just less efficient and a lot more code to do the same thing).

Example:

//cd[title != 'Unchain my heart']

or

title[. != 'Unchain my heart']
查看更多
冷血范
4楼-- · 2019-07-27 14:29

One way would be to make a preparatory sweep on the XML to pipeline it into a better structure.

The concept here is you make your own XML then evaluate it as a node-set (separate from the source XML). This is achieved via EXSLT's node-set() method'

I just knocked up this XMLPlayground session - hope it helps. As you can see, no trailing colon.

http://www.xmlplayground.com/054Mj7

查看更多
登录 后发表回答