deepsheep Posted April 28, 2010 Share Posted April 28, 2010 I am attempting to create an xsl sheet that performs well at the task of comparing attributes of elements with the same id.For example in the xml below:- - within the <oldData> tag, the productName for the record with id="2" is "Apple Red" - however, within the <newData> tag, the productName for the same id is "Apple" <data> <newData> <record id="1" productName="Orange" price="30" /> <record id="2" productName="Apple" price="34" /> <record id="3" productName="Banana" price="22" /> </newData> <oldData> <record id="1" productName="Orange" price="30" /> <record id="2" productName="Apple Red" price="90" /> </oldData></data> I've written some xsl that works, but it performs very badly when the number of attributes nudges the 10,000 mark. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" /> <xsl:key name="ids" match="//record" use="@id" /> <xsl:template match="/"> <changedAttributes> <xsl:apply-templates select="//newData//record[count(key('ids',@id)) = 2]/@*" /> </changedAttributes> </xsl:template> <xsl:template match="@*"> <xsl:variable name="value"> <xsl:value-of select="." /> </xsl:variable> <xsl:variable name="name"> <xsl:value-of select="name()" /> </xsl:variable> <xsl:variable name="id"> <xsl:value-of select="../@id" /> </xsl:variable> <xsl:variable name="oldValue"> <xsl:value-of select="//oldData//record[@id=$id]/@*[name()=$name] " /> </xsl:variable> <xsl:if test="$oldValue != $value"> <record> <xsl:attribute name="id"> <xsl:value-of select="$id" /> </xsl:attribute> <xsl:attribute name="attrName"> <xsl:value-of select="$name" /> </xsl:attribute> <xsl:attribute name="newValue"> <xsl:value-of select="$value" /> </xsl:attribute> <xsl:attribute name="oldValue"> <xsl:value-of select="$oldValue" /> </xsl:attribute> </record> </xsl:if> </xsl:template></xsl:stylesheet> I'm limited to using XSLT version 1.0. Can someone please help me with a better approach. Thanks... Link to comment Share on other sites More sharing options...
Martin Honnen Posted April 30, 2010 Share Posted April 30, 2010 It is not really possible to make suggestions on improving performance without knowing the XSLT processor you use and without suitable test data to measure the performance. Nevertheless here is a suggestion that uses a key in a different way: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:key name="k1" match="oldData/record" use="@id"/> <xsl:template match="/"> <changedAttributes> <xsl:apply-templates select="data/newData/record"/> </changedAttributes> </xsl:template> <xsl:template match="newData/record"> <xsl:variable name="oldRecord" select="key('k1', @id)"/> <xsl:if test="$oldRecord"> <xsl:apply-templates select="@*"> <xsl:with-param name="oldRecord" select="$oldRecord"/> </xsl:apply-templates> </xsl:if> </xsl:template> <xsl:template match="newData/record/@*"> <xsl:param name="oldRecord"/> <xsl:variable name="oldAtt" select="$oldRecord/@*[name() = name(current())]"/> <xsl:if test=". != $oldAtt"> <record id="{../@id}" attrName="{name()}" newValue="{.}" oldValue="{$oldAtt}"/> </xsl:if> </xsl:template></xsl:stylesheet> You will need to test whether that performance better than your approach.It could also help to use a second key for the attribute access: <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:key name="k1" match="oldData/record" use="@id"/> <xsl:key name="k2" match="oldData/record/@*" use="concat(generate-id(..), '|', name())"/> <xsl:template match="/"> <changedAttributes> <xsl:apply-templates select="data/newData/record"/> </changedAttributes> </xsl:template> <xsl:template match="newData/record"> <xsl:variable name="oldRecord" select="key('k1', @id)"/> <xsl:if test="$oldRecord"> <xsl:apply-templates select="@*"> <xsl:with-param name="oldRecord" select="$oldRecord"/> </xsl:apply-templates> </xsl:if> </xsl:template> <xsl:template match="newData/record/@*"> <xsl:param name="oldRecord"/> <xsl:variable name="oldAtt" select="key('k2', concat(generate-id($oldRecord), '|', name()))"/> <xsl:if test=". != $oldAtt"> <record id="{../@id}" attrName="{name()}" newValue="{.}" oldValue="{$oldAtt}"/> </xsl:if> </xsl:template></xsl:stylesheet> Again that is merely a suggestion as to what might help, you will need to test yourself whether it really improves performance.Please report back which XSLT processor you use and whether any of the two suggestions considerably improves performance. Link to comment Share on other sites More sharing options...
deepsheep Posted May 3, 2010 Author Share Posted May 3, 2010 Hi Martin,Thanks for taking the time to help me out. Your input has made a huge improvement!!I've tested both your suggestions against a data set with approximately 20,000 attributes. I'm using Firefox's XSLT processor with javascript.The results:- 1) my xsl - 8.45 secs2) your first xsl - 0.54 secs3) your second xsl - 0.088 secsWhat huge differences. It seems that utilising keys for accessing elements is much more efficient.Thanks again for your help. Link to comment Share on other sites More sharing options...
boen_robot Posted May 3, 2010 Share Posted May 3, 2010 FYI, if you already have 20 000 things on the client side, it's probably time you move to server side, and only let clients download the data they're going to see... or are you actually displaying all 20 000 things in a single view?!? Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.