Jump to content

Comparing Attributes


deepsheep

Recommended Posts

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

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

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

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

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...