Why is xsl:when not working as expected?

2019-08-29 07:44发布

问题:

I am a beginner to the XSLT and I am a bit confused on how to use xsl:when. Please find my sample XSLT code below with expected output and the actual output I am getting from the XSLT. Could you please suggest your solution and explain what I am doing incorrect while using xsl:when.

XML

<source>
  <bold>Hello, world.</bold>
  <italic>fine.</italic>
  <red>I am </red>
</source>

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">
    <xsl:template match="source">
            <xsl:choose>
                <xsl:when test="bold">
                    <xsl:element name="p">
                        <xsl:element name="b">
                        <xsl:value-of select="bold"></xsl:value-of>
                    </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="red">
                    <xsl:element name="p">
                        <xsl:value-of select="red"></xsl:value-of>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="italic">
                    <xsl:element name="p">
                        <xsl:element name="i">
                            <xsl:value-of select="italic"></xsl:value-of>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:element name="p">
                        <xsl:apply-templates/>
                    </xsl:element>
                </xsl:otherwise>
            </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

EXPECTED OUTPUT

<p><b>Hello, world.</b></p>
<p><i>fine.</i></p>
<p style="color:red;">I am </p>

ACTUAL OUTPUT

<p><b>Hello, world.</b></p>

回答1:

If possible can you show how we can Implement this expected output in xsl:choose?

To implement this using xsl:choose you could do something like:

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

<xsl:template match="source/*">
    <xsl:choose>
        <xsl:when test="self::bold">
            <p><b><xsl:apply-templates/></b></p>
        </xsl:when>
        <xsl:when test="self::italic">
            <p><i><xsl:apply-templates/></i></p>
        </xsl:when>
        <xsl:when test="self::red">
            <p style="color:red;"><xsl:apply-templates/></p>
        </xsl:when>
        <xsl:otherwise>
            <p><xsl:apply-templates/></p>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Note that here the template using xsl:choose matches the actual elements children of source - unlike your attempt that took place at the parent source level.

This means that each element tests itself (hence the use of the self axis) and itself only. Unlike your attempt, that tested the children of source and returned true for the first test if any child of source was <bold>.

Keep in mind that just because it is possible, it doesn't mean it's the best way to do it.



回答2:

Note: Figuring out what your input XML would have been to produce the shown output given your XSLT was an unnecessary challenge, really. Please include the input in your question next time.

Explanation: When your template matches the source element, xsl:choose finds only the first xsl:when condition whose test passes. A better organization to achieve your desired output would break the xsl:when elements out into their own xsl:templates...

Given this input XML:

<source>
  <bold>Hello, world.</bold>
  <italic>fine.</italic>
  <red>I am </red>
</source>

This XSLT:

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

  <xsl:output omit-xml-declaration="yes"/>

  <xsl:template match="bold">
    <p><b><xsl:apply-templates/></b></p>
  </xsl:template>

  <xsl:template match="red">
    <p style="color:red;"><xsl:apply-templates/></p>
  </xsl:template>

  <xsl:template match="italic">
    <p><i><xsl:apply-templates/></i></p>
  </xsl:template>

</xsl:stylesheet>

Will produce the requested output:

  <p><b>Hello, world.</b></p>
  <p><i>fine.</i></p>
  <p style="color:red;">I am </p>

But you really should add this additional template:

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

To produce this well-formed output XML:

<div>
  <p><b>Hello, world.</b></p>
  <p><i>fine.</i></p>
  <p style="color:red;">I am </p>
</div>