Reputation: 4313
I have this xml:
<root>
<row>
<number>1001461</number>
<unit>CAN</unit>
</row>
<row>
<number>1001462</number>
<unit>KG</unit>
</row>
</root>
My Xslt:
<?xml version="1.0" encoding="utf-8"?>
<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" indent="yes"/>
<xsl:param name="formid" select="60202" />
<xsl:template match="@* | node()">
<DocumentElement>
<xsl:apply-templates>
<xsl:with-param name="primarykey" select="position()"/>
</xsl:apply-templates>
</DocumentElement>
</xsl:template>
<xsl:template match="row" name="trow">
<xsl:param name="primarykey"/>
<xsl:for-each select="*">
<SaveDataTable>
<KeyName>
<xsl:value-of select="name()"/>
</KeyName>
<KeyValue>
<xsl:value-of select="."/>
</KeyValue>
<PrimaryKey>
<xsl:value-of select="concat('-',$primarykey)"/>
</PrimaryKey>
<FormId>
<xsl:value-of select="$formid"/>
</FormId>
</SaveDataTable>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Expected output: (note that the first two <PrimaryKey>
has -1 and the next two has -2, that's how I need)
<DocumentElement>
<SaveDataTable>
<KeyName>number</KeyName>
<KeyValue>1001461</KeyValue>
<PrimaryKey>-1</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>unit</KeyName>
<KeyValue>CAN</KeyValue>
<PrimaryKey>-1</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>number</KeyName>
<KeyValue>1001462</KeyValue>
<PrimaryKey>-2</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>unit</KeyName>
<KeyValue>KG</KeyValue>
<PrimaryKey>-2</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
</DocumentElement>
Basically for every <row>...</row>
the <PrimaryKey>-1</PrimaryKey>
should decrement by 1, but it's not happening. All the <PrimaryKey>
elements have a value of -1 instead of -1, -2 and so on.
===========================================
Update: I kinda get it to work but I'm not sure if it is efficient.
Working xslt (may need improvement):
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="formid" select="60202"/>
<xsl:template match="@* | node()">
<DocumentElement>
<xsl:for-each select="row">
<xsl:call-template name="trow">
<xsl:with-param name="primarykey" select="position()"/>
</xsl:call-template>
</xsl:for-each>
</DocumentElement>
</xsl:template>
<xsl:template name="trow">
<xsl:param name="primarykey"/>
<xsl:for-each select="*">
<SaveDataTable>
<KeyName>
<xsl:value-of select="name()"/>
</KeyName>
<KeyValue>
<xsl:value-of select="."/>
</KeyValue>
<PrimaryKey>
<xsl:value-of select="concat('-',$primarykey)"/>
</PrimaryKey>
<FormId>
<xsl:value-of select="$formid"/>
</FormId>
</SaveDataTable>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2
Views: 1276
Reputation: 243599
Push style solution -- similar to that of C. M. Sperberg-McQueen, but probably slightly more efficient as no xsl:number
is calculated and the position is passed as a parameter.
Another difference is that we don't need to specify the priority
attribute on any template.
Also, we don't multiply by -1.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pFormid" select="60202"/>
<xsl:template match="/*">
<DocumentElement>
<xsl:apply-templates/>
</DocumentElement>
</xsl:template>
<xsl:template match="row">
<xsl:apply-templates select="*">
<xsl:with-param name="pPos" select="position()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="row/*">
<xsl:param name="pPos"/>
<SaveDataTable>
<KeyName>
<xsl:value-of select="name()"/>
</KeyName>
<KeyValue>
<xsl:value-of select="."/>
</KeyValue>
<PrimaryKey>
<xsl:value-of select="-$pPos"/>
</PrimaryKey>
<FormId>
<xsl:value-of select="$pFormid"/>
</FormId>
</SaveDataTable>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root>
<row>
<number>1001461</number>
<unit>CAN</unit>
</row>
<row>
<number>1001462</number>
<unit>KG</unit>
</row>
</root>
the wanted, correct result is produced:
<DocumentElement>
<SaveDataTable>
<KeyName>number</KeyName>
<KeyValue>1001461</KeyValue>
<PrimaryKey>-1</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>unit</KeyName>
<KeyValue>CAN</KeyValue>
<PrimaryKey>-1</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>number</KeyName>
<KeyValue>1001462</KeyValue>
<PrimaryKey>-2</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
<SaveDataTable>
<KeyName>unit</KeyName>
<KeyValue>KG</KeyValue>
<PrimaryKey>-2</PrimaryKey>
<FormId>60202</FormId>
</SaveDataTable>
</DocumentElement>
Upvotes: 0
Reputation: 25054
Another approach (in a 'push' rather than a 'pull' style) would break the processing up into templates:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes"/>
<xsl:param name="formid" select="60202"/>
Wrap the results for the entire document in DocumentElement
.
<xsl:template match="/">
<DocumentElement>
<xsl:apply-templates/>
</DocumentElement>
</xsl:template>
For each row, get its positional number. We use xsl:number
rather than position()
because we don't want to count text nodes.
<xsl:template match="row" priority="10">
<xsl:variable name="pos">
<xsl:number count="row" level="single"/>
</xsl:variable>
<SaveDataTable>
<xsl:apply-templates>
<xsl:with-param name="pkey" select="-1 * $pos"/>
</xsl:apply-templates>
</SaveDataTable>
</xsl:template>
On each child of a row
element, emit a SaveDataTable
element containing key name, key value, primary key, and form ID.
<xsl:template match="row/*">
<xsl:param name="pkey"/>
<KeyName>
<xsl:value-of select="name()"/>
</KeyName>
<KeyValue>
<xsl:value-of select="."/>
</KeyValue>
<PrimaryKey>
<xsl:value-of select="$pkey"/>
</PrimaryKey>
<FormId>
<xsl:value-of select="$formid"/>
</FormId>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2
Reputation: 56222
You can try this solution, but I think it is less efficient than yours:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="formid" select="60202"/>
<xsl:template match="/root">
<DocumentElement>
<xsl:apply-templates select="row/*"/>
</DocumentElement>
</xsl:template>
<xsl:template match="*">
<SaveDataTable>
<KeyName>
<xsl:value-of select="name()"/>
</KeyName>
<KeyValue>
<xsl:value-of select="."/>
</KeyValue>
<PrimaryKey>
<xsl:value-of select="-1 * (count(../preceding-sibling::row) + 1)"/>
</PrimaryKey>
<FormId>
<xsl:value-of select="$formid"/>
</FormId>
</SaveDataTable>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0