XSLT 1.0 text nodes printing by default

2019-02-13 22:41发布

问题:

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>! Does anyone have a clue why this is happening?

Thank you for your understanding and your hints!

Best regards, Peter

回答1:

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


回答2:

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

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