p2p.wrox.com Forums

p2p.wrox.com Forums (http://p2p.wrox.com/index.php)
-   BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition ISBN: 978-0-470-19274-0 (http://p2p.wrox.com/forumdisplay.php?f=398)
-   -   Excluding elements in XPath (http://p2p.wrox.com/showthread.php?t=75419)

baseliner July 27th, 2009 07:29 PM

Excluding elements in XPath
 
I'm trying to exclude certain elements using XPath within an ASP.NET C# application. My XML structure is as follows:

Code:

<A>
  <B>
    <C>
      <D>
      <D>

What I want is the following:
Code:

<A>
  <B>
    <C>
      <D>

where the only <D> element left meets a certain criterion. I've been looking online and experimenting for some time now but not able to figure this out. So as a precursor, I'm trying to just exclude all <D> elements in the document with the following XPath code string that goes into the SelectNodes method in my .NET app:

"(//*)[not(self::D)]"

This doesn't work. Not sure what's the right way to do this (and I'm hoping I can do this with just XPath and not XSLT to minimize overhead in loading a stylesheet file) .. thanks!

samjudson July 28th, 2009 02:07 AM

"//*[not(self::D)]" should select all nodes except 'D' elements.

This will exclude ALL D elements. Given the example you've shown there is no way of us knowing how you want to show the 1 'D' element you have shown so we can't really help you further.

baseliner July 28th, 2009 02:34 AM

Thanks Sam. As I mentioned, I was trying to exclude ALL D elements as a precursor to the end-goal of excluding based on a criterion (I think I can manage the additional predicates if I can get the ALL exclusion to work OK). But the problem I'm having is that neither "(//*)[not(self::D)]" nor "//*[not(self::D)]" is excluding all the D elements. Below is my .NET code where I execute this XPath expression to compute newNodeList (the code is trying to generate a new XML doc where all D elements are excluded; eventually I want it to generate a new XML doc based on the values of @myAttr). When I look at the value of newDoc after the AppendChild, all the D elements are still there. Maybe it's something in this code that's incorrect? Thanks!

Code:

          XmlDocument Doc = new XmlDocument();
                    Doc.LoadXml(xmlInputString);
                    XmlNodeList nodeList = Doc.SelectNodes("/A/B/C/D/@myAttr");


                    for (int i = 0; i < nodeList.Count; i++ )
                    {
                        string myVal = nodeList.Item(i).ToString();

                        XmlDocument newDoc = new XmlDocument();
                        string query = "//*[not(self::D)]";
                       
                        XmlNodeList newNodeList = Doc.SelectNodes(query);
                        newDoc.AppendChild(newDoc.ImportNode(newNodeList[0], true));
                       
                        ... more code
                    }


mhkay July 28th, 2009 03:49 AM

Your XPath is working fine; it's what you do with the result that's the problem. Your XPath result doesn't include the D node, but it does include its parent, and when you Append the parent to the new tree, it will bring its children with it.

Why not write this whole thing in XSLT or XQuery - it's so much easier than low-level procedural DOM coding!

baseliner July 28th, 2009 12:26 PM

Thanks for pointing this out, Michael. I was trying to avoid XSLT since I need to perform this logic multiple times in the for loop so I was trying to avoid the overhead associated with loading an XSLT file from disk every time (my application's users are very sensitive to time it takes to execute). Not sure if there's a way to load the XSLT file just once prior to the for loop and then use it from memory within the for loop?

Also, the following XSLT code doesn't seem to work for this (I"m getting empty output, I think I'm doing something silly):

Code:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:template match="/">
        <xsl:apply-templates select="*"/>
    </xsl:template>
   
    <xsl:template match="*" mode="copy">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates mode="copy"/>
        </xsl:copy>
    </xsl:template>
   
    <xsl:template match="D" mode="copy"/>
</xsl:stylesheet>

I haven't programmed with XQuery yet but it looks like it's inline code (no external file needed) so I'll look into this more.. How does it compare with XSLT in terms of performance (given that the XSLT file has to be loaded from disk)?

Thanks!

samjudson July 28th, 2009 12:30 PM

Why do you think that XSLT has to be loaded from a string?

Check out the XslCompiledTransform.Load method, which can be passed a XmlReader - you can load this from an embedded string, or from a file or whatever.

The following would copy all elements apart from the 'D' element:

<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>

<xsl:template match="D"/>

mhkay July 28th, 2009 12:44 PM

I think every XSLT processor I know of allows you (a) to read the stylesheet from a string in your host application, and (b) to compile it once and execute it repeatedly. The same is true of XQuery - there's no intrinsic reason why one should be faster than the other.

However, rather than calling an XSLT transformation repeatedly from procedural code, why not be more radical, and get rid of the procedural code entirely?

mhkay July 28th, 2009 12:46 PM

Oh, I forgot: the problem with your code is that the first template does apply-templates without specifying mode="copy". You don't actually need modes here at all.

baseliner July 29th, 2009 11:34 AM

Michael and Sam - thanks a lot for your help! The compiled transform was just what I needed and I have it all working now.

Michael - not sure I follow your suggestion of getting rid of the procedural code completely.. this XSL transformation is within an ASP.NET webservice app that does other stuff after the transform is done..


All times are GMT -4. The time now is 11:29 PM.

Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.