Reputation: 2786
So I have xml that look like this
<Data>
<History>
<z r="1" c1="BILL" c2="123" c3="02/20/2006" c4="100.00" c5=".00" c6=".00" c7="100.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
<z r="2" c1="PAYCODE" c2="456" c3="03/16/2006" c4="-100.00" c5=".00" c6=".00" c7="-100.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
<z r="3" c1="Total" c2="123" c3="02/20/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
<z r="4" lcdesc=""/>
<z r="5" c1="BILL" c2="124" c3="03/27/2006" c4="13000.00" c5="400.00" c6=".00" c7="13400.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
<z r="6" c1="PAYCODE" c2="457" c3="04/13/2006" c4="-13000.00" c5="-400.00" c6=".00" c7="-13400.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
<z r="7" c1="Total" c2="124" c3="03/27/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
<z r="8" lcdesc=""/>
<z r="9" c1="BILL" c2="125" c3="04/28/2006" c4="1000.00" c5=".00" c6=".00" c7="1000.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL"/>
<z r="10" c1="PAYCODE" c2="458" c3="05/10/2006" c4="-1000.00" c5=".00" c6=".00" c7="-1000.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT"/>
<z r="11" c1="Total" c2="125" c3="04/28/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total"/>
<z r="12" lcdesc=""/>
</History>
</Data>
And I want to put some information from the c1="BILL" z node on the subsequent lines. I want to get the invoice number (c2) and the invoice date (c3). So I figured in my template I'd declare a variable and do a select for either the current node if c1 = 'BILL' or the preceding-sibling where c1 = 'BILL'. While it works for the first node and it's subsequent lines, on the second bill line it puts the previous bill lines information. So something isn't right. Can you help?
Here is my non-working xslt:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
<xsl:output indent="no"/>
<xsl:template match="/">
<History>
<xsl:apply-templates select="//History/z"/>
</History>
</xsl:template>
<xsl:template match="/PageData/History/z">
<xsl:variable name="invnode" select="current()[@c1 = 'BILL'] | preceding-sibling::z[@c1 = 'BILL'][1]" />
<z>
<xsl:for-each select="@*">
<!--get the attribute name-->
<xsl:variable name="nodename">
<xsl:value-of select="name()"/>
</xsl:variable>
<xsl:attribute name="{$nodename}"><xsl:value-of select="."/></xsl:attribute>
</xsl:for-each>
<xsl:attribute name="invoice"><xsl:value-of select="$invnode/@c2" /></xsl:attribute>
<xsl:if test="string-length($invnode/@c3) >= 10">
<xsl:attribute name="invdate1"><xsl:value-of select="concat(substring($invnode/@c3, 7), substring($invnode/@c3, 1, 2), substring($invnode/@c3, 4, 2))" /></xsl:attribute>
<xsl:attribute name="invdate2"><xsl:value-of select="concat(substring($invnode/@c3, 7), '-', substring($invnode/@c3, 1, 2), '-', substring($invnode/@c3, 4, 2))" /></xsl:attribute>
</xsl:if>
</z>
</xsl:template>
</xsl:stylesheet>
The output I want is as follows:
<Data>
<History>
<z r="1" c1="BILL" c2="123" c3="02/20/2006" c4="100.00" c5=".00" c6=".00" c7="100.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
<z r="2" c1="PAYCODE" c2="456" c3="03/16/2006" c4="-100.00" c5=".00" c6=".00" c7="-100.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
<z r="3" c1="Total" c2="123" c3="02/20/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
<z r="4" lcdesc="" invoice="123" invdate1="20060220" invdate2="2006-02-20" />
<z r="5" c1="BILL" c2="124" c3="03/27/2006" c4="13000.00" c5="400.00" c6=".00" c7="13400.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
<z r="6" c1="PAYCODE" c2="457" c3="04/13/2006" c4="-13000.00" c5="-400.00" c6=".00" c7="-13400.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
<z r="7" c1="Total" c2="124" c3="03/27/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
<z r="8" lcdesc="" invoice="124" invdate1="20060327" invdate2="2006-03-27" />
<z r="9" c1="BILL" c2="125" c3="04/28/2006" c4="1000.00" c5=".00" c6=".00" c7="1000.00" c8=".00" c9=".00" c10=".00" lcdesc="BILL" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
<z r="10" c1="PAYCODE" c2="458" c3="05/10/2006" c4="-1000.00" c5=".00" c6=".00" c7="-1000.00" c8=".00" c9=".00" c10=".00" lcdesc="PAYMENT" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
<z r="11" c1="Total" c2="125" c3="04/28/2006" c4=".00" c5=".00" c6=".00" c7=".00" c8=".00" c9=".00" c10=".00" lcdesc="Total" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
<z r="12" lcdesc="" invoice="125" invdate1="20060428" invdate2="2006-04-28" />
</History>
</Data>
Upvotes: 1
Views: 1216
Reputation: 116982
Try it this way?
XSLT 1.0
<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="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="z">
<xsl:variable name="invnode" select="current()[@c1='BILL'] | preceding-sibling::z[@c1 = 'BILL'][1][not(current()/@c1='BILL')]" />
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="invoice">
<xsl:value-of select="$invnode/@c2" />
</xsl:attribute>
<xsl:attribute name="invdate1">
<xsl:value-of select="concat(substring($invnode/@c3, 7), substring($invnode/@c3, 1, 2), substring($invnode/@c3, 4, 2))" />
</xsl:attribute>
<xsl:attribute name="invdate2">
<xsl:value-of select="concat(substring($invnode/@c3, 7), '-', substring($invnode/@c3, 1, 2), '-', substring($invnode/@c3, 4, 2))" />
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Explanation:
This expression:
current()[@c1='BILL']
selects the current node, provided that the current node is an invoice; otherwise it returns an empty node-set.
This expression:
preceding-sibling::z[@c1 = 'BILL'][1][not(current()/@c1='BILL')]"
returns the nearest preceding-sibling that is an invoice, provided that the current node is not an invoice; otherwise it returns an empty node-set.
Therefore, in the union of these two expressions, one of the sub-sets will always be empty:
If the current node is an invoice, the left-hand side returns the current node, and the right-hand side is empty.
If the current node is not an invoice, the left-hand side is empty, and the right-hand side returns the nearest preceding-sibling that is an invoice.
Thus the union will only ever contain a single node, and the order is of no significance.
Upvotes: 2
Reputation: 12729
How about ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" >
<xsl:output indent="yes" omit-xml-declaration="yes" encoding="utf8" />
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="z">
<z invoice="{(.|preceding-sibling::z)[@c1='BILL'][last()]/@c2}">
<xsl:apply-templates select="(.|preceding-sibling::z)[@c1='BILL'][last()]/@c3" mode="inv-date" />
<xsl:apply-templates select="@*|node()"/>
</z>
</xsl:template>
<xsl:template match="@c3" mode="inv-date" />
<xsl:template match="@c3[string-length(.) >= 10]" mode="inv-date">
<xsl:attribute name="invdate1"><xsl:value-of select="concat(substring(., 7), substring(., 1, 2), substring(., 4, 2))" /></xsl:attribute>
<xsl:attribute name="invdate2"><xsl:value-of select="concat(substring(., 7), '-', substring(., 1, 2), '-', substring(., 4, 2))" /></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2