Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT 1.0 text nodes printing by default

I have looked at XSL xsl:template match="/" but the match pattern that triggered my question is not mentioned there.

I have a rather complex XML structure:

<?xml version="1.0" encoding="UTF-8"?>
<MATERIAL_DATA>
<LOG>
    <USER>Peter</USER>
    <DATE>2011-02-18</DATE>
    <MATERIALS>
        <item>
            <MATNR>636207</MATNR>
            <TEXTS>
                <item>
                    <TEXT>granola bar 40gx24</TEXT>
                </item>
            </TEXTS>
            <PRICES>
                <item>
                    <MATNR>636207</MATNR>
                    <COST>125.78</COST>
                </item>
            </PRICES>
            <SALESPRICES>
                <item>
                    <B01>
                        <MATNR>636207</MATNR>
                        <CURR>CZK</CURR>
                        <DATBI>9999-12-31</DATBI>
                        <DATAB>2010-10-05</DATAB>
                    </B01>
                    <B02>
                        <item>
                            <PRICE>477.60</PRICE>
                            <KUNNR>234567</KUNNR>
                        </item>
                    </B02>
                </item>
            </SALESPRICES>
        </item>
    </MATERIALS>
</LOG>
</MATERIAL_DATA>

Now if I apply the following XSLT, my output looks correct:

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

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

<xsl:template match="B02">
    <xsl:element name="Mi">
        <xsl:value-of select="item/KUNNR"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

I get the output:

<?xml version="1.0" encoding="UTF-8"?>
<Mi>234567</Mi>

But if I apply the XSLT:

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

<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

<xsl:template match="/*">
    <xsl:element name="MenuItems">
        <xsl:apply-templates select="LOG/MATERIALS/item/SALESPRICES/item"/>
    </xsl:element>
 </xsl:template>
 
<xsl:template match="B02">
    <xsl:element name="Mi">
        <xsl:value-of select="item/KUNNR"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

the output looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<MenuItems>
                    
                        636207
                        CZK
                        9999-12-31
                        2010-10-05
<Mi>234567</Mi>
</MenuItems>

All values from the element <B01> are in the output! But why - I am not matching <B01>!? How does

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

make the output come out correctly? All I am doing with this is match all nodes or attributes and apply-templates to everything or all attributes. But in my opinion it should not make a difference to when I exactly match <B01>! Why is this happening?

like image 287
Peter Avatar asked Feb 22 '11 20:02

Peter


People also ask

What is text () in XSLT?

The <xsl:text> element is used to write literal text to the output. Tip: This element may contain literal text, entity references, and #PCDATA.

What is output format in XSLT?

The method attribute can have the value xml , html , or text for XML, HTML, and text output, respectively.

What is the output of an XSLT processor?

An XSLTProcessor applies an XSLT stylesheet transformation to an XML document to produce a new XML document as output.

What is number () in XSLT?

If the argument is a boolean value, the value true is converted to the number 1 ; the value false is converted to the number 0 . If the argument is a node-set, the node-set is converted to a string as if it were passed to the string() function, then that string is converted to a number like any other string.


2 Answers

XSLT includes the following default templates (among others):

<!-- applies to both element nodes and the root node -->
<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

<!-- copies values of text and attribute nodes through -->
<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

In your first stylesheet you're implicitly matching all text nodes with node(), thus overriding the default action. Then, in the B2 template, you output your target value and apply no further templates, which stops processing.

In the second stylesheet, you explicitly apply templates to all children of LOG/MATERIALS/item/SALESPRICES/item, causing the default templates to process the nodes you don't explicitly handle. Because you explicitly handle B2 without applying templates to its children, the default templates are never invoked for those nodes. But the default templates are applied to the children of B1.

Adding the following template to your second stylesheet would override the default action for text nodes:

<xsl:template match="text()|@*"></xsl:template>

With the following result:

<?xml version="1.0" encoding="UTF-8"?>
<MenuItems><Mi>234567</Mi></MenuItems>

More:

  • http://www.w3.org/TR/xslt#built-in-rule
like image 167
Wayne Avatar answered Sep 29 '22 09:09

Wayne


Looks like you are running into the built in template rules.

Specifically the text rule - this will copy text nodes if not overridden.

like image 25
Oded Avatar answered Sep 29 '22 09:09

Oded