Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
| Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
  #1 (permalink)  
Old July 15th, 2003, 07:20 PM
Registered User
 
Join Date: Jun 2003
Location: Menlo Park, CA, USA.
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
Default XPath: set operation with a disjoint node set

input file:
<foo type="a"/>
<foo type="b"/>
<foo type="c"/>
<bar type="a"/>
<bar type="b"/>

Now, say I've got node sets:
<xsl:variable name="A" select="foo"/>
<xsl:variable name="B" select="bar"/>

And I'd like to select any <foo>s with types not found in the set of <bar>s. In this example, that would be just the 3rd <foo>. How would I do this?

My first thought was to select:
$A[string(@type) != string($B/@type)]

...but that will only select nodes from A whose type is not "a" (the first <foo>'s type).

Any clues?

Thanks,
Rich
  #2 (permalink)  
Old July 16th, 2003, 02:31 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

You don't want or need string function and you need to change from != to not:
$A[not(@type = $B/@type)]

not means there are no matches, i.e. no @type in $B matches the @type of $A, != means they all don't match, if any non-match can be found then include the node.
If you're xml is:

Code:
<root>
<foo type="a"/>
<foo type="b"/>
<foo type="c"/>
<bar type="a"/>
<bar type="b"/>
</root>
and xsl is:
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:variable name="A" select="root/foo"/>
    <xsl:variable name="B" select="root/bar"/>
    <xsl:for-each select="$A[@type != $B/@type]">
      <xsl:text>#x0a;</xsl:text><xsl:copy-of select="."/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
Run this and then change select to:
$A[@type != $B/@type]

to see difference.

--

Joe
  #3 (permalink)  
Old July 16th, 2003, 04:47 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

Sorry, the initial xsl should have been:
Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:variable name="A" select="root/foo"/>
    <xsl:variable name="B" select="root/bar"/>
    <xsl:for-each select="$A[not(@type = $B/@type)]">
      <xsl:text>#x0a;</xsl:text><xsl:copy-of select="."/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
I pasted the wrong version...

--

Joe
  #4 (permalink)  
Old July 16th, 2003, 05:20 AM
Friend of Wrox
 
Join Date: Jun 2003
Location: Nor Hachin, Kotayk, Armenia.
Posts: 147
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via Yahoo to armmarti
Default

The whole problem is in the "magical" properties of the "!=" operator and in the use of the "string" function here.
Look why your code is not working:
The function "string" returns the string value of the first node when the argument is a nodeset. So in your XPath expression you compare the context node's attribute's string-value to the first "foo" element's attribute value.
Even
Code:
$A[@type != $B/@type]
is not working again due to the rules for "!=" operator. Actually, "=" operator "returns" true for nodesets NS1 and NS2 as arguments when there is a node n1 in NS1 and node n2 in NS2 such that their string-values are the same. And "!=" is not a logical negation of "=": for 2 nodesets "!=" "returns" true if there is at least one pair of (n1, n2) [n1 is in NS1 and n2 is in NS2] such that their string-values are not the same. Therefore there are situations when "not(NS1 = NS2)" is not equivalent to "NS1 != NS2".
That's why you have to use "not" instead of "!=" here and you have to compare nodesets instead of using"string" function.

The working XPath expression is
Code:
$A[not(@type = $B/@type)]
Regards,
Armen
  #5 (permalink)  
Old July 16th, 2003, 01:30 PM
Registered User
 
Join Date: Jun 2003
Location: Menlo Park, CA, USA.
Posts: 5
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Wow, thanks for setting me straight, guys!
  #6 (permalink)  
Old May 5th, 2008, 10:33 AM
Registered User
 
Join Date: May 2008
Location: , , .
Posts: 4
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I have a similar issue but I'm having a hard time trying to figure out how to make it work. I'm trying to get the union of two node sets with one having priority.

Example:
<defaults>
  <color>red</color>
  <size>10</size>
  <flavor>cherry</flavor>
</defaults>

<config>
  <color>black</color>
  <flavor>blueberry</flavor>
</config>

So, it seems like I should be able to do something like this:

<xsl:variable name="class" select="defaults"></xsl:param>
<xsl:variable name="instance" select="config"></xsl:param>

<xsl:for-each select="$class[not(name() != $instance/name())]">
    <xsl:text>#x0a;</xsl:text><xsl:copy-of select="."/>
</xsl:for-each>

<xsl:for-each select="$instance">
        <xsl:copy-of select="."/>
</xsl:for-each>

The results should end up something like this then:

  <size>10</size>
  <color>black</color>
  <flavor>blueberry</flavor>

I know I've probably presented this too simply so please let me know what other info I could provide to help it make sense. The main issue seems to be the predicate not being allowed to match on name().
  #7 (permalink)  
Old May 5th, 2008, 10:58 AM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

You first need to say whether you are using XSLT 1.0 or XSLT 2.0.

In 2.0 this is best treated as a grouping problem:

<xsl:for-each-group select="defaults/*, config/*" group-by="node-name()">
  <xsl:copy-of select="current-group()[last()]"/>
</xsl:for-each>

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
  #8 (permalink)  
Old May 6th, 2008, 09:24 AM
Registered User
 
Join Date: May 2008
Location: , , .
Posts: 4
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I'm using XSLT 1.0


Similar Threads
Thread Thread Starter Forum Replies Last Post
Illegal operation on empty result set. amisrivas JSP Basics 2 February 19th, 2008 11:49 PM
Filtering out from a node set anothervbaddict XSLT 1 May 22nd, 2007 08:42 AM
"OPERATION NOT ALLOWED AFTERRESULT SET IS CLOSED" picky JSP Basics 3 October 12th, 2006 03:26 PM
OPERATION NOT ALLOWED AFTERRESULT SET IS CLOSED" picky Java Databases 0 October 4th, 2006 05:36 AM
PWS0007 - Operation result set not found tkwhk SQL Server 2000 0 August 14th, 2006 02:32 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.