tags:

views:

41

answers:

2

Hi, I am working on an XSL development and I am in need of knowing the NOT IN equivalent in XPATH. I am presenting the XML and XSL in the simplest format which would be understandable to all.

<?xml-stylesheet type="text/xsl" href="XSL.xsl"?>
<Message>
    <Customers>
        <Customer pin="06067">1</Customer>
        <Customer pin="06068">2</Customer>
        <Customer pin="06069">3</Customer>
        <Customer pin="06070">4</Customer>
        <Customer pin="06072">5</Customer>
    </Customers>
    <Addresses>
        <Address pin1="06067">A</Address>
        <Address pin1="06068">B</Address>
        <Address pin1="06069">C</Address>
    </Addresses>
</Message>

XSL

<xsl:template match="/Message">
    <html>
        <body>
            <h4>Existing Customers</h4>
            <table>
                <xsl:apply-templates select="//Customers/Customer[@pin = //Addresses/Address/@pin1]"></xsl:apply-templates>
            </table>

            <h4>New Customers</h4>
            <table>
                <!--This place need to be filled with new customers-->
            </table>
        </body>
    </html>
</xsl:template>

<xsl:template match="Customer" name="Customer">
    <xsl:variable name="pin" select="./@pin"></xsl:variable>
    <tr>
        <td>
            <xsl:value-of select="."/>
            <xsl:text> is in </xsl:text>
            <xsl:value-of select="//Addresses/Address[@pin1=$pin]"/>
        </td>
    </tr>
</xsl:template>

In the above XSLT, under the commented area, i need to match and display the customers who's address is not existing in the Addresses/Address node.

Please help find an XPath expression that would match the Customers who are NOT IN the Addresses Node set. (Any alternate could also help)

+5  A: 

In XPath 1.0:

/Message/Customers/Customer[not(@pin=/Message/Addresses/Address/@pin1)]
Alejandro
Wow it works well. Thank you so much.
SaravananArumugam
@Saravanandss: You are wellcome!
Alejandro
+1. This is fundamental XPath knowledge -- unfortunately the OP is happy that "it works" and would probably not delve further to understand what actually is happening... :(
Dimitre Novatchev
I think the fundamental what-is-happening that @Dimitre refers to is the fact that the "=" operator is an "existentially quantified comparison" operator, that is, `A = B` tells you whether there is a pair of values, one in nodeset A and one in nodeset B, that are equal to each other. In the above example, A is @pin which addresses only one node (because it is in the context of just one `Customer`), while the B path addresses many nodes.
LarsH
+2  A: 

An alternative to the good answer by @Alejandro, which I upvoted, is the following transformation, which uses keys and will be more efficient if the number of existing customers is big:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:key name="kexistingByPin"
         match="Address" use="@pin1"/>

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

 <xsl:template match="/">
   <xsl:apply-templates select=
    "*/*/Customer[not(key('kexistingByPin', @pin))]"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<Message>
    <Customers>
        <Customer pin="06067">1</Customer>
        <Customer pin="06068">2</Customer>
        <Customer pin="06069">3</Customer>
        <Customer pin="06070">4</Customer>
        <Customer pin="06072">5</Customer>
    </Customers>
    <Addresses>
        <Address pin1="06067">A</Address>
        <Address pin1="06068">B</Address>
        <Address pin1="06069">C</Address>
    </Addresses>
</Message>

the wanted, correct answer is produced:

<Customer pin="06070">4</Customer>
<Customer pin="06072">5</Customer>
Dimitre Novatchev