XSLT to process XML with multiple namespaces - xml-namespaces

I have XML with multiple namespaces but the elements are the same in all of them. I need to transform this XML to JSON but I am not sure how to dynamically pass/change namespaces without repeating the same XSLT code with different namespace.
Therefore I am getting the data in the output only for the namespace that i have defined.
Below is my sample XML -
<?xml version="1.0" encoding="utf-8"?>
<root>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction">
<wd:Report_Entry>
<wd:company>TESTCOMPANY</wd:company>
<wd:revenue_stream>X</wd:revenue_stream>
<wd:customer_id>XCUSTOMER</wd:customer_id>
<wd:invoice_id>201900000035</wd:invoice_id>
<wd:post_date>2019-05-01</wd:post_date>
<wd:initiatedby>Test Data</wd:initiatedby>
<wd:amount>-100</wd:amount>
<wd:trans_date>2019-04-22</wd:trans_date>
<wd:legacy>false</wd:legacy>
<wd:exported>2019-05-01T12:13:02.773-07:00</wd:exported>
<wd:reason>Credit Invoice</wd:reason>
</wd:Report_Entry>
</wd:Report_Data>
<wd:Report_Data xmlns:wd="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction">
<wd:Report_Entry>
<wd:company>TESTCOMPANY</wd:company>
<wd:revenue_stream>X</wd:revenue_stream>
<wd:customer_id>XCUSTOMER</wd:customer_id>
<wd:invoice_id>201900000020</wd:invoice_id>
<wd:post_date>2019-05-01</wd:post_date>
<wd:amount>30</wd:amount>
<wd:trans_date>2019-04-01</wd:trans_date>
<wd:legacy>false</wd:legacy>
<wd:exported>2019-05-01T12:13:03.030-07:00</wd:exported>
<wd:reason>Disputed Amount</wd:reason>
</wd:Report_Entry>
</wd:Report_Data>
</root>
Below is the XSLT i have created -
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction" xmlns:wd1="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction" exclude-result-prefixes="xs" version="3.0">
<xsl:mode streamable="yes" on-no-match="shallow-skip"/>
<xsl:output method="text" encoding="UTF-8" indent="no"/>
<xsl:template match="root/wd:Report_Data">
<xsl:iterate select="wd:Report_Entry/copy-of()">
<!--Define Running Totals for Statistics -->
<xsl:param name="TotalCount" select="0"/>
<xsl:param name="TotalAmount" select="0"/>
<!--Write Statistics -->
<xsl:on-completion>
<xsl:text>{"Stats": </xsl:text>
<xsl:text>{"Total Count": </xsl:text>
<xsl:value-of select="$TotalCount"/>
<xsl:text>,</xsl:text>
<xsl:text>"Total Amount": </xsl:text>
<xsl:value-of select="$TotalAmount"/>
<xsl:text>}}</xsl:text>
</xsl:on-completion>
<!--Write Details -->
<xsl:text>{"id": "</xsl:text>
<xsl:value-of select="wd:id"/>
<xsl:text>",</xsl:text>
<xsl:text>"company": "</xsl:text>
<xsl:value-of select="wd:company"/>
<xsl:text>",</xsl:text>
<xsl:text>"trans_type": "</xsl:text>
<xsl:value-of select="wd:trans_type"/>
<xsl:text>",</xsl:text>
<xsl:text>"revenue_stream": "</xsl:text>
<xsl:value-of select="wd:revenue_stream"/>
<xsl:text>",</xsl:text>
<xsl:text>"customer_id": "</xsl:text>
<xsl:value-of select="wd:customer_id"/>
<xsl:text>",</xsl:text>
<xsl:text>"invoice_id": "</xsl:text>
<xsl:value-of select="wd:invoice_id"/>
<xsl:text>",</xsl:text>
<xsl:text>"post_date": "</xsl:text>
<xsl:value-of select="wd:post_date"/>
<xsl:text>",</xsl:text>
<xsl:text>"initiatedby": "</xsl:text>
<xsl:value-of select="wd:initiatedby"/>
<xsl:text>",</xsl:text>
<xsl:text>"amount": </xsl:text>
<xsl:value-of select="wd:amount"/>
<xsl:text>,</xsl:text>
<xsl:text>"trans_date": "</xsl:text>
<xsl:value-of select="wd:trans_date"/>
<xsl:text>",</xsl:text>
<xsl:text>"legacy": </xsl:text>
<xsl:value-of select="wd:legacy"/>
<xsl:text>,</xsl:text>
<xsl:text>"exported": "</xsl:text>
<xsl:value-of select="wd:exported"/>
<xsl:text>"}</xsl:text>
<!--Store Running Totals -->
<xsl:next-iteration>
<xsl:with-param name="TotalCount" select="$TotalCount + 1"/>
<xsl:with-param name="TotalAmount" select="$TotalAmount + wd:amount"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
Expected Result -
{
"id": "",
"company": "TESTCOMPANY",
"trans_type": "",
"revenue_stream": "",
"customer_id": "XCUSTOMER",
"invoice_id": "201900000035",
"post_date": "2019-05-01",
"initiatedby": "Test Data",
"amount": -100,
"trans_date": "2019-04-22",
"legacy": false,
"exported": "2019-05-01T12:13:02.773-07:00"
}
{
"id": "",
"company": "TESTCOMPANY",
"trans_type": "",
"revenue_stream": "X",
"customer_id": "XCUSTOMER",
"invoice_id": "201900000035",
"post_date": "2019-05-01",
"initiatedby": "Test Data",
"amount": -100,
"trans_date": "2019-04-22",
"legacy": false,
"exported": "2019-05-01T12:13:02.773-07:00"
} {
"Stats": {
"Total Count": 2,
"Total Amount": -200
}
}

Here is an example using the wild card selector *:foo and accumulators to produce JSON output, as the only streaming XSLT 3 processor I know of is Saxon 9 EE I have also used the extension element saxon:array, as it makes it easier to create JSON output with arrays. In pure XSLT 3 where you only have the XPath 3.1 array constructors [] and array { } it is always a bit cumbersume to produce array data on the fly from XSLT instructions like xsl:apply-templates:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
extension-element-prefixes="saxon"
exclude-result-prefixes="#all" version="3.0">
<xsl:mode use-accumulators="#all" streamable="yes"/>
<xsl:output method="json" indent="yes"/>
<xsl:accumulator name="entry-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="*:Report_Data/*:Report_Entry" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="amount-sum" as="xs:decimal" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="*:Report_Data/*:Report_Entry/*:amount/text()"
select="$value + xs:decimal(.)"/>
</xsl:accumulator>
<xsl:template match="root">
<xsl:map>
<xsl:map-entry key="local-name()">
<saxon:array>
<xsl:apply-templates select="*:Report_Data/*:Report_Entry"/>
<xsl:sequence
select="
map {
'Stats': map {
'Total Count': accumulator-after('entry-count'),
'Total Amount': accumulator-after('amount-sum')
}
}"
/>
</saxon:array>
</xsl:map-entry>
</xsl:map>
</xsl:template>
<xsl:template match="*:Report_Entry">
<xsl:sequence
select="
map {
'id': string(*:id),
'amount': xs:decimal(*:amount)
}"
/>
</xsl:template>
</xsl:stylesheet>
Unfortunately with two version of Saxon 9 EE available in oXygen 21, 9.8.0.12 and 9.9.1.1, this only passes the streamability analysis in 9.9, so there the result is
{
"root": [
{
"amount": -100,
"id": null
},
{
"amount": 30,
"id": null
},
{
"Stats": {
"Total Count": 2,
"Total Amount": -70 } }
] }
Of course the approach to use a wildcard *:foo can also be used with your xsl:iterate approach:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wd="urn:com.workday.report/INT1111a_CR_REV_FINRA_Connect_AR_Adjustment_Transaction" xmlns:wd1="urn:com.workday.report/INT1111b_CR_REV_FINRA_Connect_AR_Writeoff_Transaction" exclude-result-prefixes="xs" version="3.0">
<xsl:mode streamable="yes" on-no-match="shallow-skip"/>
<xsl:output method="text" encoding="UTF-8" indent="no"/>
<xsl:template match="root">
<xsl:iterate select="*:Report_Data/*:Report_Entry/copy-of()">
<!--Define Running Totals for Statistics -->
<xsl:param name="TotalCount" select="0"/>
<xsl:param name="TotalAmount" select="0"/>
<!--Write Statistics -->
<xsl:on-completion>
<xsl:text>{"Stats": </xsl:text>
<xsl:text>{"Total Count": </xsl:text>
<xsl:value-of select="$TotalCount"/>
<xsl:text>,</xsl:text>
<xsl:text>"Total Amount": </xsl:text>
<xsl:value-of select="$TotalAmount"/>
<xsl:text>}}</xsl:text>
</xsl:on-completion>
<!--Write Details -->
<xsl:text>{"id": "</xsl:text>
<xsl:value-of select="*:id"/>
<xsl:text>",</xsl:text>
<xsl:text>"company": "</xsl:text>
<xsl:value-of select="*:company"/>
<xsl:text>",</xsl:text>
<xsl:text>"trans_type": "</xsl:text>
<xsl:value-of select="*:trans_type"/>
<xsl:text>",</xsl:text>
<xsl:text>"revenue_stream": "</xsl:text>
<xsl:value-of select="*:revenue_stream"/>
<xsl:text>",</xsl:text>
<xsl:text>"customer_id": "</xsl:text>
<xsl:value-of select="*:customer_id"/>
<xsl:text>",</xsl:text>
<xsl:text>"invoice_id": "</xsl:text>
<xsl:value-of select="*:invoice_id"/>
<xsl:text>",</xsl:text>
<xsl:text>"post_date": "</xsl:text>
<xsl:value-of select="*:post_date"/>
<xsl:text>",</xsl:text>
<xsl:text>"initiatedby": "</xsl:text>
<xsl:value-of select="*:initiatedby"/>
<xsl:text>",</xsl:text>
<xsl:text>"amount": </xsl:text>
<xsl:value-of select="*:amount"/>
<xsl:text>,</xsl:text>
<xsl:text>"trans_date": "</xsl:text>
<xsl:value-of select="*:trans_date"/>
<xsl:text>",</xsl:text>
<xsl:text>"legacy": </xsl:text>
<xsl:value-of select="*:legacy"/>
<xsl:text>,</xsl:text>
<xsl:text>"exported": "</xsl:text>
<xsl:value-of select="*:exported"/>
<xsl:text>"}</xsl:text>
<!--Store Running Totals -->
<xsl:next-iteration>
<xsl:with-param name="TotalCount" select="$TotalCount + 1"/>
<xsl:with-param name="TotalAmount" select="$TotalAmount + *:amount"/>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/ej9EGcA/1

Related

Combining XML Nodes into a single node with an XSLT

I'm trying to edit some XML with a transform but I'm struggling to achieve my desired results.
I have some XML:
<FX>
<Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY" />
<Attribute NAME="N1" VALUE="V1" />
<Attribute NAME="N2" VALUE="V2" />
<Attribute NAME="N3" VALUE="V3" />
</FX>
And I want to transform it to look like:
<FX>
<Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY" />
<Attribute NAME="N1, N2, N3" VALUE="V1,V2,V3" />
</FX>
Is this possible? Can anyone offer any suggestions on how to do this with a transform?
You can use the following, Asp.NET compatable, XSLT-1.0 stylesheet to perform an XSLT transformation from your source XML to your destination XML:
<?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" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/FX">
<xsl:copy>
<xsl:copy-of select="Order" />
<Attribute>
<xsl:attribute name="NAME">
<xsl:for-each select="Attribute">
<xsl:value-of select="#NAME" />
<xsl:if test="position() != last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
<xsl:attribute name="VALUE">
<xsl:for-each select="Attribute">
<xsl:value-of select="#VALUE" />
<xsl:if test="position() != last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
</Attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Its output is:
<FX>
<Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY"/>
<Attribute NAME="N1, N2, N3" VALUE="V1,V2,V3"/>
</FX>
In general, if you want to transform some nodes but keep the rest you use the identity transformation template as the starting point and then add templates that change those nodes you want to change:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="FX/Attribute[1]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="FX/Attribute[position() > 1]"/>
<xsl:template match="FX/Attribute[1]/#*">
<xsl:attribute name="{name()}">
<xsl:for-each select=". | ../following-sibling::Attribute/#*[name() = name(current())]">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyH9rNk

Recursive loop in Xslt 1.0

I am working on a recursive loop. Below is the sample code.
The loop gets executed only once. It is not executing until it reaches the maximum count (20 times).
Can you please help if this is an issue?
Thanks,
Raj
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xdoxslt='http://www.oracle.com/XSL/Transform/java/oracle.apps.xdo.template.rtf.XSLTFunctions'>
<xsl:output method="text" omit-xml-declaration="yes" />
<xsl:variable name="nl">
<xsl:value-of select="'
'"/>
<xsl:text xml:space="preserve">
</xsl:text>
</xsl:variable>
<xsl:template match = "ARCHIVE_CHEQUE_WRITER">
<xsl:for-each select="CHEQUE">
<xsl:variable name="ded" select="./AC_DEDUCTIONS"/>
<xsl:variable name="dedcount" select="count($ded)"/>
<xsl:variable name="earn" select="./AC_EARNINGS"/>
<xsl:variable name="earncount" select="count($earn)"/>
<xsl:value-of select="$dedcount"/>
<xsl:value-of select="$nl"/>
<xsl:value-of select="$earncount"/>
<xsl:value-of select="$nl"/>
<xsl:text>Starting New Record*******</xsl:text>
<xsl:value-of select="$nl"/>
<xsl:for-each select="./AC_DEDUCTIONS">
<xsl:text>This is sample template</xsl:text>
<xsl:value-of select="$nl"/>
</xsl:for-each>
<xsl:value-of select="$nl"/>
<xsl:value-of select="$nl"/>
<xsl:call-template name="recursive-loop">
<xsl:with-param name="pCount" select="20"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="recursive-loop">
<xsl:param name="pStart" select="1"/>
<xsl:param name="pCount"/>
<xsl:text> value of parameters</xsl:text>
<xsl:value-of select="$nl"/>
<xsl:value-of select="$pStart"/>
<xsl:value-of select="$pCount"/>
<xsl:value-of select="$nl"/>
<xsl:if test="$pCount > pStart">
<xsl:call-template name="recursive-loop">
<xsl:with-param name="pStart" select="$pStart+1"/>
<xsl:with-param name="pCount" select="$pCount"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
You forgot to use $ for variable pStart in the template
<xsl:if test="$pCount > $pStart">
<xsl:call-template name="recursive-loop">
<xsl:with-param name="pStart" select="$pStart+1"/>
<xsl:with-param name="pCount" select="$pCount"/>
</xsl:call-template>
</xsl:if>

XSLT Informal Time Translation

I'm currently working on a translation of documents for importing as XML to another system, and this involves the translation of a quite informal representation of time, such as the following:
<estimated-time>15 mins<estimated-time>
And I need to translate this to something like the following:
<tr:estimated_time>00:15:00</tr:estimated_time>
I've messed around with tokenizing, substrings, and the various time functions and haven't been able to come up with anything, though I am quite inexperienced in XSLT.
Following Jirka's answer, I tried the following:
<xsl:template match="estimated-time">
<tr:estimated_time>
<xsl:value-of select="time:parseTime(./text(), 'hours')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="time:parseTime(./text(), 'mins')"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="time:parseTime(./text(), 'seconds')"/>
</tr:estimated_time>
</xsl:template>
<xsl:function name="time:parseTime">
<xsl:param name="testedString"/>
<xsl:param name="lookingFor"/>
<xsl:variable name="tokens" select="tokenize($testedString, ' ')" />
<xsl:variable name="out">
<xsl:choose>
<xsl:when test="$tokens[. = $lookingFor]">
<xsl:variable name="pos" select="index-of($tokens, $lookingFor)-1"/>
<xsl:value-of select="$tokens[$pos]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>00</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="if (string-length($out)=1) then concat('0', $out) else $out"/>
</xsl:function>
Which always resulted in:
<tr:estimated_time>00:00:00</tr:estimated_time>
Any assistance would be greatly appreciated.
Update: it works! There were some weird newlines all over the original that I hadn't spotted, which were preventing it from working.
Might be there is a more sofisticated or cleaner solution but using tokenizing it should be done for example in this way
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:tst="test">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<times>
<xsl:apply-templates select="times/time" />
</times>
</xsl:template>
<xsl:template match="time">
<time>
<xsl:value-of select="tst:getSomething(.,'hours')" />
<xsl:text>:</xsl:text>
<xsl:value-of select="tst:getSomething(.,'mins')" />
<xsl:text>:</xsl:text>
<xsl:value-of select="tst:getSomething(.,'sec')" />
</time>
</xsl:template>
<xsl:function name="tst:getSomething">
<xsl:param name="testedString" />
<xsl:param name="lookingFor" />
<xsl:variable name="tokens" select="tokenize($testedString, ' ')" />
<xsl:variable name="tmp">
<xsl:choose>
<xsl:when test="$tokens[. = $lookingFor]">
<xsl:variable name="pos" select="index-of($tokens, $lookingFor) - 1" />
<xsl:value-of select="$tokens[$pos]" />
</xsl:when>
<xsl:otherwise>
<xsl:text>00</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="if (string-length($tmp) = 1) then concat('0', $tmp) else $tmp" />
</xsl:function>
</xsl:stylesheet>
It produces output
<?xml version="1.0" encoding="UTF-8"?>
<times>
<time>05:30:00</time>
<time>00:05:00</time>
</times>

Issues with publishing an RSS feed

I'm having trouble publishing an RSS feed from my Umbraco site. I found this Umbraco.TV video and tried to follow the instructions there using an XSLT selector to select all nodes of a give type, like so:
umbraco.library.GetXmlAll()/node [#nodeTypeAlias='Alias]/node
As sugested here but that didn't work. Apparently the schema has changed or something. When this didn't work I looked for a plugin to do this kind of stuff and was amazed to find just 2 plugins, both of them with little-to-no documentation and neither seemed to work (first plugin, second plugin).
So once and for all, I'd like to have a definite answer - how does one publish an RSS feed in Umbraco?
Here's an XSLT that we use for News Items RSS (News Items are under a News Page). Let me know if that helps. I also have versions for Blogs.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rssdatehelper="urn:rssdatehelper"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:msxml="urn:schemas-microsoft-com:xslt"
xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets ">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<!-- Update these variables to modify the feed -->
<xsl:variable name="RSSNoItems" select="/macro/RSSNoItems"/>
<xsl:variable name="RSSTitle" select="/macro/RSSTitle"/>
<xsl:variable name="SiteURL" select="concat('http://',umbraco.library:RequestServerVariables('HTTP_HOST'))"/>
<xsl:variable name="RSSDescription" select="/macro/RSSDescription"/>
<xsl:variable name="source" select="/macro/source"/>
<!-- This gets all news and events and orders by updateDate to use for the pubDate in RSS feed -->
<xsl:variable name="pubDate">
<xsl:for-each select="umbraco.library:GetXmlNodeById($source)/* [#isDoc and string(umbracoNaviHide) != '1']">
<xsl:sort select="./newsDate" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="./newsDate" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<!-- change the mimetype for the current page to xml -->
<xsl:value-of select="umbraco.library:ChangeContentType('text/xml')"/>
<xsl:text disable-output-escaping="yes"><?xml version="1.0" encoding="UTF-8"?></xsl:text>
<rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
>
<channel>
<title>
<xsl:value-of select="$RSSTitle"/>
</title>
<link>
<xsl:value-of select="$SiteURL"/>
</link>
<pubDate>
<xsl:value-of select="$pubDate"/>
</pubDate>
<generator>umbraco v4</generator>
<description>
<xsl:value-of select="$RSSDescription"/>
</description>
<language>en</language>
<xsl:for-each select="umbraco.library:GetXmlNodeById($source)/* [#isDoc and string(umbracoNaviHide) != '1']">
<xsl:sort select="./newsDate" order="descending" />
<xsl:if test="position() <= $RSSNoItems">
<xsl:call-template name="RSSitem">
<xsl:with-param name="node" select="current()"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</channel>
</rss>
</xsl:template>
<xsl:template match="node">
<xsl:if test="position() <= $RSSNoItems">
<item>
<title>
<xsl:value-of select="#nodeName"/>
</title>
<link>
<xsl:value-of select="$SiteURL"/>
<xsl:value-of select="umbraco.library:NiceUrl(#id)"/>
</link>
<pubDate>
<xsl:value-of select="umbraco.library:FormatDateTime(./newsDate,'r')" />
</pubDate>
<guid>
<xsl:value-of select="$SiteURL"/>
<xsl:value-of select="umbraco.library:NiceUrl(#id)"/>
</guid>
<content:encoded>
<xsl:value-of select="concat('<![CDATA[ ', ./bodyText,']]>')" disable-output-escaping="yes"/>
</content:encoded>
</item>
</xsl:if>
</xsl:template>
<xsl:template name="RSSitem">
<xsl:param name="node"/>
<item>
<title>
<xsl:value-of select="$node/#nodeName"/>
</title>
<link>
<xsl:value-of select="$SiteURL"/><xsl:value-of select="umbraco.library:NiceUrl($node/#id)"/>
</link>
<pubDate>
<xsl:value-of select="umbraco.library:FormatDateTime(./newsDate,'r')"/>
</pubDate>
<dc:creator><xsl:value-of select="#writerName"/></dc:creator>
<xsl:for-each select="umbraco.library:Split($node/categories, ',')/value">
<xsl:sort data-type="text" order="ascending"/>
<category>
<xsl:value-of select="current()"/>
</category>
</xsl:for-each>
<guid>
<xsl:value-of select="$SiteURL"/><xsl:value-of select="umbraco.library:NiceUrl($node/#id)"/>
</guid>
<description>
<xsl:value-of select="concat('<![CDATA[ ', $node/summary,']]>')" disable-output-escaping="yes"/>
</description>
<content:encoded>
<xsl:value-of select="concat('<![CDATA[ ', $node/bodyText,']]>')" disable-output-escaping="yes"/>
</content:encoded>
</item>
</xsl:template>
</xsl:stylesheet>

xsl to retrieve other attribute values and append values into one attribute

To start:
<test style="font:2px;color:#FFFFFF" bgcolor="#CCCCCC" TOPMARGIN="5">style</test>
Using XSLT/XPATH, I copy everything over from my document
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
But I'm not sure how to get this result using XSLT/XPATH:
<test style="background-color:#CCCCCC; margin-top:1;font:2px;color:#FFFFFF">style</test>
I think I'm failing at the XPATH. This is my attempt at just retrieving bgColor:
<xsl:template match="#bgColor">
<xsl:attribute name="style">
<xsl:text>background-color:</xsl:text>
<xsl:value-of select="."/>
<xsl:text>;</xsl:text>
<xsl:value-of select="../#style"/>
</xsl:attribute>
</xsl:template>
Unfortunately, even this breaks when style is placed after bgColor in the original document. How can I append these deprecated attribute values into one inline style attribute?
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<test style="{#style};background-color:{#bgcolor};margin-top:{#TOPMARGIN}">
<xsl:value-of select="."/>
</test>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<test style="font:2px;color:#FFFFFF"
bgcolor="#CCCCCC" TOPMARGIN="5">style</test>
produces the wanted, correct result:
<test style="font:2px;color:#FFFFFF;background-color:#CCCCCC;margin-top:5">style</test>
Explanation: Use of AVT.
May be not the best way, but it works:
<xsl:template match="test">
<xsl:element name="{name()}">
<xsl:apply-templates select="#*[name() != 'bgcolor']"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:copy/>
</xsl:template>
<xsl:template match="#style">
<xsl:attribute name="style">
<xsl:value-of select="."/>
<xsl:text>;background-color:</xsl:text>
<xsl:value-of select="../#bgcolor"/>
</xsl:attribute>
</xsl:template>

Resources