Copy XML replacing selected node contents in XSLT (2.0)

Here should go questions about transforming XML with XSLT and FOP.
michuk
Posts: 8
Joined: Tue Dec 11, 2007 1:52 pm

Copy XML replacing selected node contents in XSLT (2.0)

Post by michuk »

I'm wondering if there is a way in XSLT (2.0 is fine) to copy some part of XML replacing some node values at the same time. Here is an example to visualize the matter:

<invoice>
[...]
<apples>
<apple id=1"><price>100</price></apple>
<apple id=2"><price>120</price></apple>
<apple id=3"><price>90</price></apple>
</apples>
<update-prices>
<modification><price apple-id="1">112</price></modification>
<modification><price apple-id="2">132</price></modification>
<modification><price apple-id="3">102</price></modification>
</update-prices>
[...]
</invoice>

So, I would like to prepare an XSLT stylesheet which copies the whole <invoice> element with its children, but replacing the apple prices [isn't $100 for an apple high enough? damn inflation...] with new prices that are included in the <update-prices> element.
It would be nice to delete the <update-prices> element in the resulting document as well.

So the result XML should look like this:

<invoice>
[...]
<apples>
<apple id=1"><price>112</price></apple>
<apple id=2"><price>132</price></apple>
<apple id=3"><price>102</price></apple>
</apples>
[...]
</invoice>

I would not like to map every possible node that can appear in the XML file accordning to the schema. I would just like to copy and paste the XML replacing some of the values using a pattern. Is there a non-hacker way to do this in XSLT?
jkmyoung
Posts: 89
Joined: Mon Mar 06, 2006 10:13 pm

Post by jkmyoung »

Start off with a copy template
---
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
---
Then have an apple template.
Notice the check for a price modification. Unfortunately this method is a little bit slower, since we have to check every apple for a modification, but I don't entirely see a workaround.
---
<xsl:template match="apple">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:choose>
<xsl:when test="//update-prices/modification/price/@apple-id[.=current()/@id]">
<xsl:apply-templates select="//update-prices/modification/price[@apple-id=current()/@id]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
---
Don't process the update-prices node directly
---
<xsl:template match="update-prices"/>
---
And add a modification to the price node template so we don't copy the apple-id attribute
---
<xsl:template match="price">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
---

As a side, you can also add a key to the price-modifications if you have a lot of them, and it is worth indexing.
michuk
Posts: 8
Joined: Tue Dec 11, 2007 1:52 pm

Post by michuk »

Thanks for your tips. It's been a long time since I last coded XSLT. I'm starting to recall stuff now :)
george
Site Admin
Posts: 2095
Joined: Thu Jan 09, 2003 2:58 pm

Post by george »

You can use a key to match modification elements by apple-id. That will considerably speed up the processing.

Best Regards,
George
George Cristian Bina
Post Reply