Reputation: 183
I've been at this a while now - have got the XSLT working mostly, removed unwanted nodes and attributes, but there's one part of it that is throwing me.
The idea is that I have 3 elements with 1 attribute each, and I want to move the attributes from the second and third elements into the first element (and drop the 2nd & 3rd elements).
[Snippet 1]From:
<info>
<id name="Tim"/>
<address addr="1 Witchwood Close"/>
<phone num="01234 567 891"/>
</info>
[Snippet 2]To:
<info>
<id name="Tim" addr="1 Witchwood Close" num="01234 567 891"/>
</info>
xslt is currently (and has outputted xml fragment as above):
<xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<!-- remove 'fullname' node to promote sub-nodes -->
<xsl:template match="fullname">
<xsl:apply-templates"/>
</xsl:template>
<!-- Promote 'phone' node to 'info' (from 'address' sub-node) -->
<xsl:template match="address[phone]">
<xsl:copy>
<xsl:apply-templates select="node()[not(self::phone)]|@*"/>
</xsl:copy>
<xsl:apply-templates select="phone"/>
</xsl:template>
<!-- Drop unwanted elements & attributes -->
<xsl:template match="addressline2|addressline3|postcode|@middlename"/>
</xsl:stylesheet>
Any pointers?
Many thanks
EDIT
Including the snippet of XSLT from Eero, it didn't seem to do anything (i.e I got the same XML as in [Snippet 1]), however playing around with it:
<xsl:template match="id">
<xsl:copy>
<xsl:apply-templates select="@* | following-sibling::*/>
</xsl:copy>
</xsl:template>
produced:
<info>
<id name="Tim">
<address addr="1 Witchwood Close"/>
<phone num="01234 567 891"/>
</id>
<address addr="1 Witchwood Close"/>
<phone num="01234 567 891"/>
</info?
If following sibling is changed to
following-sibling::*|@*
I get the same result. So why is the following-sibling statement that contains '/' rather than '|' not doing any transforms? It's clear I'm missing something simple.
EDIT 2 (inc solution)
I think I've sorted it. Realising that I needed to gen up on XPath as (what was probably pretty obvious by now!) I didn't really know wtf was going on, I came up with this snippet:
<xsl:template match="id">
<xsl:copy>
<xsl:apply-templates select="@name | following-sibling::*//@addr"/>
<xsl:apply-templates select="@name | @addr | following-sibling::*//@num"/>
</xsl:copy>
</xsl:template>
Which produces:
<info>
<id name="Tim" addr="1 Witchwood Close" num="01234 567 891"/>
</info>
Which is exactly what I was after! I think it's probably not the most elegant way of doing things, but it seems to get the job done… with one minor exception. Occasionally, the output will be (with no apparent reason as to why that one and not another):
<info>
<id name="Tim" addr="1 Witchwood Close" num="01234 567 891"/>
<address addr="1 Witchwood Close"/>
</info>
despite the node being removed after the copy. This is not a massive issue, as the SQL XML import is set to only import fields on a row identified by 'id', so ignores the 'address' row.
I have marked Eero's answer as 'accepted' as that was what gave me the direction to eventually solve it, however ugly my code actually is. Comments welcome on how it could be made less of a kludge!
Thank you!
Upvotes: 0
Views: 840
Reputation: 2585
A simple stylesheet like this should do the trick:
<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="*"/>
<xsl:template match="id">
<xsl:copy>
<!--
Apply the attributes of the current node and the attributes of all
following siblings (in this case, <address> and <phone>)
-->
<xsl:apply-templates select="@* | following-sibling::*/@*"/>
</xsl:copy>
</xsl:template>
<!-- Drop the <address> and <phone> elements -->
<xsl:template match="address | phone"/>
<!--
Identity transform: copy attributes and elements from input document to output
document as is
-->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1