ttaaoossuu
ttaaoossuu

Reputation: 7884

XSLT modify attribute value at EXACT element by passing the original value to a template

I'm struggling to get this abomination called XSLT to work. I need to get an EXACT attribute at EXACT path, pass its original value to a template and rewrite this value with the result from the template.

I'm having a file like this:

<?xml version="1.0" encoding="windows-1251"?>
<File>
  <Document ReportYear="17">
        ...
        ...
  </Document>
</File>

So I made an XSLT like this:

<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

    <xsl:output method="xml" encoding="windows-1251" indent="yes" />

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

    <xsl:template name="formatYear">
        <xsl:param name="year" />
        <xsl:value-of select="$year + 2000" />
    </xsl:template>

    <xsl:template match="File/Document">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:attribute name="ReportYear">
                <xsl:call-template name="formatYear">
                    <xsl:with-param name="year" select="@ReportYear" />
                </xsl:call-template>
            </xsl:attribute>
        </xsl:copy>
        <xsl:apply-templates />
    </xsl:template>

</xsl:stylesheet>

This works fine except it closes the <Document> tag immediately and places its content immediately after itself.

Also, can I address the ReportYear attribute value without repeating it twice? I tried current() but it didn't work.

Upvotes: 0

Views: 103

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167516

If you need to process the contents of the Document element with apply-templates and want to keep the result of the applied templates as the children then you need to move the apply-templates inside of the copy:

<xsl:template match="File/Document">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:attribute name="ReportYear">
            <xsl:call-template name="formatYear">
                <xsl:with-param name="year" select="@ReportYear"/>
            </xsl:call-template>
        </xsl:attribute>
        <xsl:apply-templates/>
    </xsl:copy>     
</xsl:template>

Not sure why you haven't simply used

<xsl:template match="File/Document/@ReportYear">
   <xsl:attribute name="{name()}">
      <xsl:value-of select=". + 2000"/>
   </xsl:attribute>
</xsl:template>

together with the identity transformation template.

Upvotes: 1

Tomalak
Tomalak

Reputation: 338208

If you're closing <xsl:copy> before applying templates to the remainder of the content of <Document>, then of course <Document> will be closed before the remainder of the content of <Document> appears in the output.

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" encoding="windows-1251" indent="yes" />

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

    <xsl:template match="Document">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:attribute name="ReportYear">
                <xsl:value-of select="@ReportYear + 2000" />
            </xsl:attribute>
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

outputs

<?xml version="1.0" encoding="windows-1251"?>
<File>
  <Document ReportYear="2017">
        ...
        ...
  </Document>
</File>

I don't think an extra template just for adding 2000 to @ReportYear is necessary. But if you must, you can streamline the whole thing like so

<xsl:template name="formatYear">
    <xsl:param name="year" select="@ReportYear" />   <!-- you can define a default value -->
    <xsl:value-of select="$year + 2000" />
</xsl:template>

and

<xsl:attribute name="ReportYear">
    <xsl:call-template name="formatYear" />  <!-- ...and can use it implicitly here -->
</xsl:attribute>

Upvotes: 1

Related Questions