views:

178

answers:

1

I have the following XSL template (I omitted the template for Organization, let me know if it's necessary):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;     
<xsl:output method="html" indent="yes" omit-xml-declaration="yes"/>

<xsl:template match="SOAP-ENV:Body/*[local-name()='Publisher']">
    <html>      
        <xsl:call-template name="body" />
    </html>     
</xsl:template>
    <xsl:template name="body">
    <BODY>
        <br/>
        <center>
            <font face="arial" size="2">
                <b>Publisher <xsl:value-of select="*[local-name()='Organization']/*[local-name()='PublisherData']/*[local-name()='PublisherName']"/>
                </b>
            </font>
        </center>
        <br/>
        <xsl:apply-templates select="*[local-name()='Organization']"/>
    </BODY>
</xsl:template>
</xsl:stylesheet>

The previous template generates the output I want, it's generating the tags containing the output generated by the "body" template. The issue I'm having is that before the opening tag I'm getting text output from a previous node. Not sure why this is happening since I'm not selecting these other nodes. For example:

<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
        xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
        <SOAP-ENV:Header>
            <n1>abc</n1>
                <n2>def</n2>
        </SOAP-ENV:Header>
        <SOAP-ENV:Body>
            <Publisher>
                          <!--Child nodes here -->
                    </Publisher>
            </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

Given the previous sample XML fragment, my output would contain what I would expect of formatting the Publisher element, but I'm also getting the text nodes of the children of the SOAP-ENV:Header node.

I only want to transform the contents of the Publisher element, but in the output I'll get:

abc
def
//Expected output transforming Publisher goes here

My question is, why abc and def are being selected?

+3  A: 

The output is being generated because of the built in template rules which:

  • copy text nodes to output
  • apply templates to elements

The relevant section of the XSLT 1.0 Recommendation is included below.

You may just want a do nothing template for the SOAP-ENV:Header.

<xsl:template match="SOAP-ENV:Header">
</xsl:template>

Some of the elements in your source document are not in a namespace. In an XPath expression, elements that do not have a namespace may be referred to by their literal local name. You only need to use of local-name() if you do not know the namespace for an element.

Here is a modified version of your stylesheet. It starts with a template to match / and then explicitly select the nodes we want.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;

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

    <xsl:template match="/">
        <html>      
            <xsl:apply-templates select="SOAP-ENV:Body/Publisher" />
        </html>     
    </xsl:template>

    <xsl:template name="Publisher">
        <body>
            <br/>
            <center>
                <font face="arial" size="2">
                    <b>
                        <xsl:text>Publisher </xsl:text>
                        <xsl:value-of select="Organization/PublisherData/PublisherName"/>
                    </b>
                </font>
            </center>
            <br/>
            <xsl:apply-templates select="Organization"/>
        </body>
    </xsl:template>
</xsl:stylesheet>

Built-in Template Rules

There is a built-in template rule to allow recursive processing to continue in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule:

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

There is also a built-in template rule for each mode, which allows recursive processing to continue in the same mode in the absence of a successful pattern match by an explicit template rule in the stylesheet. This template rule applies to both element nodes and the root node. The following shows the equivalent of the built-in template rule for mode m.

<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

There is also a built-in template rule for text and attribute nodes that copies text through:

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

The built-in template rule for processing instructions and comments is to do nothing.

<xsl:template match="processing-instruction()|comment()"/>

The built-in template rule for namespace nodes is also to do nothing. There is no pattern that can match a namespace node; so, the built-in template rule is the only template rule that is applied for namespace nodes.

The built-in template rules are treated as if they were imported implicitly before the stylesheet and so have lower import precedence than all other template rules. Thus, the author can override a built-in template rule by including an explicit template rule.

Lachlan Roche
@Lachlan: I suggest you tell him why his XSL didn't work, not just show him how to make it work.
John Saunders
Great! This worked, but I don't get it, why using a nothing template for SOAP-ENV:Header solved the problem? Thanks!
Abel Morelos
Got it, thanks!
Abel Morelos
@Lachlan: thanks. +1.
John Saunders
@Lachlan: I fail to see the usefulness of `local-name()` in this context.
Tomalak
@Tomalak; you're right given the previous example, I just suppressed the namespaces and prefixes (I have a bunch of them) for clarity.
Abel Morelos