Reputation: 1285
I have similar incoming XML from some hardware:
<invoice>
<field name="item">Item 1;Item 2;Item 3</field>
<field name="price">32.0;192.2;12.0</field>
<field name="quantity">1;4;2</field>
</invoice>
Which I need to convert similar to this:
<invoice>
<item>
<desc>Item 1</desc>
<price>32.0</price>
<quantity>1</quantity>
</item>
<item>
<desc>Item 1</desc>
<price>192.0</price>
<quantity>4</quantity>
</item>
<item>
<desc>Item 3</desc>
<price>12.0</price>
<quantity>2</quantity>
</item>
</invoice>
At the moment I've experimented with str:tokenize(), but the main problem is constructing a simple loop. My knowledge about XSLT is very basic and my work in progress is about that far:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:str="http://exslt.org/strings">
<xsl:output method="xml" indent="yes" version="1.0" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="inovice">
<xsl:param name="separator" select="';'"/>
<xsl:param name="desc" select="str:tokenize(field[@name='item'],$separator)"/>
<xsl:param name="price" select="str:tokenize(field[@name='price'],$separator)"/>
<xsl:param name="quantity" select="str:tokenize(field[@name='quantity'],$separator)"/>
<xsl:param name="count" select="count($desc)"/>
<!-- some loop goes here -->
</xsl:template>
</xsl:stylesheet>
Upvotes: 3
Views: 3096
Reputation: 1788
A simple XSLT 2.0 stylesheet that iterates over all items and selects the corresponding prices/quantities according to the current position, could look like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="invoice">
<xsl:variable name="fields" select="field"/>
<invoice>
<xsl:for-each select="tokenize(field[@name='item'], ';')">
<xsl:variable name="pos" select="position()"/>
<item>
<desc>
<xsl:value-of select="."/>
</desc>
<price>
<xsl:value-of select="tokenize($fields[@name='price'], ';')[position()=$pos]"/>
</price>
<quantity>
<xsl:value-of select="tokenize($fields[@name='quantity'], ';')[position()=$pos]"/>
</quantity>
</item>
</xsl:for-each>
</invoice>
</xsl:template>
</xsl:stylesheet>
If you want to use XSLT 1.0 together with the EXSLT extension module strings, the stylesheet must be modified only slighly:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="invoice">
<xsl:variable name="fields" select="field"/>
<invoice>
<xsl:for-each select="str:tokenize(field[@name='item'], ';')">
<xsl:variable name="pos" select="position()"/>
<item>
<desc>
<xsl:value-of select="."/>
</desc>
<price>
<xsl:value-of select="str:tokenize($fields[@name='price'], ';')[position()=$pos]"/>
</price>
<quantity>
<xsl:value-of select="str:tokenize($fields[@name='quantity'], ';')[position()=$pos]"/>
</quantity>
</item>
</xsl:for-each>
</invoice>
</xsl:template>
</xsl:stylesheet>
Upvotes: 3