Reputation: 17932
I've got the following XML structure :
<Article>
<id>1</id>
<line>L11</line>
<line>L12</line>
<line>L13</line>
</Article>
<Article>
<id>2</id>
<line>L21</line>
<line>L22</line>
<line>L23</line>
</Article>
I want to use XSLT to iterate over all the lines of only one article at a time so that I can achieve the following structure : (every article is converted into an order, and its lines are converted to line structures in the new structure)
<orders>
<order>
<id>1</id>
<order_line>L11</order_line>
<order_line>L12</order_line>
<order_line>L13</order_line>
</order>
<order>
<id>2</id>
<order_line>L21</order_line>
<order_line>L22</order_line>
<order_line>L23</order_line>
</order>
</orders>
Upvotes: 2
Views: 1193
Reputation: 243599
This complete XSLT 1.0 transformation:
<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:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<orders>
<xsl:apply-templates/>
</orders>
</xsl:template>
<xsl:template match="Article">
<order>
<xsl:apply-templates/>
</order>
</xsl:template>
<xsl:template match="line">
<order_line>
<xsl:apply-templates/>
</order_line>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document (wrapped into a single top element to make it well-formed):
<t>
<Article>
<id>1</id>
<line>L11</line>
<line>L12</line>
<line>L13</line>
</Article>
<Article>
<id>2</id>
<line>L21</line>
<line>L22</line>
<line>L23</line>
</Article>
</t>
produces the wanted, correct result:
<orders>
<order>
<id>1</id>
<order_line>L11</order_line>
<order_line>L12</order_line>
<order_line>L13</order_line>
</order>
<order>
<id>2</id>
<order_line>L21</order_line>
<order_line>L22</order_line>
<order_line>L23</order_line>
</order>
</orders>
Explanation:
The identity rule copies every node "as-is".
The identity rule is overriden by three templates, each of which matches a particular kind of element (the top element, Article
and line
) and simply renames the element, respectively to: orders
, order
and order_line
.
Upvotes: 2
Reputation: 2234
With XSLT, try to think of the task at hand as a set of patterns or rules you have to match, and the actions to be taken each time such a pattern is encountered. You can usually let the runtime worry about looping, etc, to discover patterns.
In your specific case, you've described two patterns you want special logic. Every time there is the element Article
, you wish to apply the rule to change its name to order
.
Every time the element line
is encountered as a child of Article
, replace it with order_line
. For any other pattern, you just want to copy the contents as they were in the original document:
<!-- Match element Article, and whenever it's encountered, insert an 'order' element, and copy the contents of Article -->
<xsl:template match="Article">
<order> <xsl:apply-templates/> </order>
</xsl:template>
<!-- Match element 'line', and whenever it's encountered, insert an 'order_line' element, and copy the contents -->
<xsl:template match="Article/line">
<order_line> <xsl:apply-templates/> </order_line>
</xsl:template>
<!-- Match any other element we haven't specified explicity, and copy it verbatim -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
Upvotes: 4
Reputation: 60424
The correct way to do "looping" in XSLT is to let the XSLT processor walk the document tree for you. Anything else is fighting against the nature of XSLT.
Here's a (near-) complete solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<orders><xsl:apply-templates/></orders>
</xsl:template>
<xsl:template match="Article">
<order>
<id><xsl:value-of select="id"/></id>
<xsl:apply-templates select="line"/>
</order>
</xsl:template>
<xsl:template match="Article/line">
<order_line><xsl:value-of select="."/></order_line>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 14610
Try this, but I am not a xslt expert :)
<?xml version="1.0" encoding="utf-8"?>
<xsl:template match="/" name="A">
<orders>
<xsl:for-each select="Articles/Article">
<order>
<id>
<xsl:value-of select="id" />
</id>
<xsl:for-each select="line">
<order_line>
<xsl:value-of select="node()"></xsl:value-of>
</order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
</xsl:template>
Upvotes: 0
Reputation: 1461
This is just of the top of my head but I think you can do it this way:
<orders>
<xsl:for-each select="//Article">
<order>
<id><xsl:value-of select="./id"/></id>
<xsl:for-each select="./line">
<order_line><xsl:value-of select="child::text()" /></order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
Anyway, it should be enough to get you started. There is only a for-each loop in XSLT though, no for loop with a counter.
Upvotes: 1
Reputation:
This is a straightforward transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<orders>
<xsl:apply-templates/>
</orders>
</xsl:template>
<xsl:template match="Article">
<order>
<xsl:apply-templates/>
</order>
</xsl:template>
<xsl:template match="line">
<order_line>
<xsl:apply-templates/>
</order_line>
</xsl:template>
<xsl:template match="id">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
EDIT: A general root element rule.
Upvotes: 3
Reputation: 1
Your source XML file is not well-formed. For clarity, my response assumes you've wrapped it in a element. This is a pretty trivial XML transformation so I've included some comments explaining what each line of code is doing.
<!-- change the template match xpath to whatever the root xpath is in your document -->
<xsl:template match="root">
<orders>
<!-- loop over each Article -->
<xsl:for-each select="Article">
<order>
<!-- pass id through -->
<xsl:copy-of select="id"/>
<!-- loop over each line -->
<xsl:for-each select="line">
<!-- create an <order_line> node -->
<order_line>
<!-- get the value of the current <line> -->
<xsl:value-of select="."/>
</order_line>
</xsl:for-each>
</order>
</xsl:for-each>
</orders>
</xsl:template>
Upvotes: 0