Reputation: 7884
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
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
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