Comparing node sets

D

Doug

I need to compare two "address" structures within a document, and
perform some action if they are not equal.

The XML document is a purchase order, with an address at both the
header and line item level:

<Order>
<Header>
... other header level stuff ...
<Address>
<AddressLine>address 1</AddressLine>
<AddressLine>address 2</AddressLine>
<CityName>city</CityName>
<PostalCode>post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</Header>
<Body>
<LineItem>
... other line item stuff ...
<Address>
<AddressLine>address 1</AddressLine>
<AddressLine>address 2</AddressLine>
<CityName>city</CityName>
<PostalCode>post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</LineItem>
<LineItem>
... other line item stuff ...
<Address>
<AddressLine>somewhere different</AddressLine>
<CityName>other city</CityName>
<PostalCode>other post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</LineItem>
</Body>
</Order>


For line items where the address is the same as the header level
address I need take no action. For addresses which differ I need to
populate an output attribute with a value. Therefore, ideally I'm
looking for an expression that I can use in the test clause of an "if"
element.

How can I compare the two address structures? I want "true" to mean
both structures are completely identical in element name, order and
value. In the above example the first line item's address is identical
to the header level, the second is different.

Is it possible to do this with XSLT? Can the XML be accessed in a
string format (like the .xml method of a node in DOM), so that I can
compare the resulting strings?

Thanks in advance for any help.

Doug
 
M

Marrow

Hi Doug,

You could try copying the two node-sets into two RTFs (result tree
fragments) and then comparing the two RTFs, e.g. something along the lines
of...

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="Order">
<xsl:variable name="setHdr">
<xsl:copy-of select="Header/Address"/>
</xsl:variable>
<xsl:variable name="setAdd1">
<xsl:copy-of select="Body/LineItem[1]/Address"/>
</xsl:variable>
<xsl:variable name="setAdd2">
<xsl:copy-of select="Body/LineItem[2]/Address"/>
</xsl:variable>
<xsl:text>(Header/Address = Body/LineItem[1]/Address) ? </xsl:text>
<xsl:value-of select="$setHdr = $setAdd1"/>
<xsl:text>
</xsl:text>
<xsl:text>(Header/Address = Body/LineItem[2]/Address) ? </xsl:text>
<xsl:value-of select="$setHdr = $setAdd2"/>
</xsl:template>
</xsl:stylesheet>

That may be sufficient for what you need.
The <xsl:strip-space elements="*"/> is required in case you have white-space
preservation turned on for the input XML - otherwise the additional
indentation on the deeper addresses will be preserved and cause a result of
false for both comparisons.
Although even that may have a pit-fall - if you should have an explicit
xml:space="preserve" in the XML that preserves the whitespace indentation.
For example...

<Order xml:space="preserve">
<Header>
... other header level stuff ...
<Address>
<AddressLine>address 1</AddressLine>
<AddressLine>address 2</AddressLine>
<CityName>city</CityName>
<PostalCode>post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</Header>
<Body>
<LineItem>
... other line item stuff ...
<Address>
<AddressLine>address 1</AddressLine>
<AddressLine>address 2</AddressLine>
<CityName>city</CityName>
<PostalCode>post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</LineItem>
<LineItem>
... other line item stuff ...
<Address>
<AddressLine>somewhere different</AddressLine>
<CityName>other city</CityName>
<PostalCode>other post code</PostalCode>
<PostalCountry>country</PostalCountry>
</Address>
</LineItem>
</Body>
</Order>

In that case you'd need to make the stylesheet a little more complex, e.g.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:eek:utput method="text"/>
<xsl:template match="Order">
<xsl:variable name="setHdr">
<xsl:apply-templates select="Header/Address" mode="rtfcopy"/>
</xsl:variable>
<xsl:variable name="setAdd1">
<xsl:apply-templates select="Body/LineItem[1]/Address" mode="rtfcopy"/>
</xsl:variable>
<xsl:variable name="setAdd2">
<xsl:apply-templates select="Body/LineItem[2]/Address" mode="rtfcopy"/>
</xsl:variable>
<xsl:text>(Header/Address = Body/LineItem[1]/Address) ? </xsl:text>
<xsl:value-of select="$setHdr = $setAdd1"/>
<xsl:text>
</xsl:text>
<xsl:text>(Header/Address = Body/LineItem[2]/Address) ? </xsl:text>
<xsl:value-of select="$setHdr = $setAdd2"/>
</xsl:template>

<xsl:template match="*" mode="rtfcopy">
<xsl:copy>
<xsl:apply-templates select="node() | @*" mode="rtfcopy"/>
</xsl:copy>
</xsl:template>

<xsl:template match="@* | comment() | processing-instruction()"
mode="rtfcopy">
<xsl:copy/>
</xsl:template>

<xsl:template match="text()" mode="rtfcopy">
<xsl:if test="normalize-space(.)">
<xsl:copy/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

NB. the modes are only there so that the copying templates don't interfere
with any other templates you might have.

Hope this helps
Marrow
http://www.marrowsoft.com - home of Xselerator (XSLT IDE and debugger)
http://www.topxml.com/Xselerator
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top