0
votes

hope you can help me out. I'm searching for an generic XSLT match template that matches all Elements of a certain Name after to first occurance. In this example everything should be copied with an identity transform, the first occurance of "line" Needs "do something" logic and all "line" Elements after the first one should be ignored. "line" Elements can occure infinite times in theory.

Sample Source XML:

<?xml version="1.0" encoding="UTF-8"?>
<sampledoc>
    <header1>a1</header1>
    <header2>b1</header2>
    <header3>c1</header3>
    <line>a2</line>
    <line>b2</line>
    <line>c2</line>
    <line>c3</line>
    <line>c4</line>
    <footer>bye</footer>
</sampledoc>

I would expect something like following::line[1] but not get it to work. So this is my "static" Version:

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

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

    <xsl:template match="line[1]">
        <!-- Do something --> 
    </xsl:template>

    <xsl:template match="line[2]"/>
    <xsl:template match="line[3]"/>
    <xsl:template match="line[4]"/>
    <xsl:template match="line[5]"/>
    <xsl:template match="line[6]"/>
    <xsl:template match="line[7]"/>



</xsl:stylesheet>

Thanks!

2
Can you edit your question to show the expected output in this case. Thanks!Tim C

2 Answers

1
votes

You need to learn about template matching priorities. In particular, a template matching an element with a condition has a higher priority than just matching the element name on its own. (Specifically, a template match of line[1] with have a priority of 0.5, but a template matching just line has a priority of 0.)

What this means is you just need the following templates.

<xsl:template match="line[1]">
    <newline /> 
</xsl:template>

<xsl:template match="line"/>

So, for the first line element, although both templates could match it, the first one has the higher priority, so that will be used in all cases.

See https://www.w3.org/TR/xslt-10/#conflict for more details.

Try this XSLT

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

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

    <xsl:template match="line[1]">
        <newline /> 
    </xsl:template>

    <xsl:template match="line"/>
</xsl:stylesheet>

(Note that your template matching / is not necessary in this case, as XSLT's built-in templates would do the same thing)

0
votes

Use <xsl:template match="line[position() > 1]"/> to not process any second, third, fourth line child element.