Using XPath to exclude nodes

Mean MrMustard

Diamond Member
Jan 5, 2001
3,144
10
81
Below is the xml structure I'm working with. I need to exclude the InvoiceDistributions, but can't figure out how to do it. I also need to exclude the APHeaders based on certain criteria.

I tried: Invoices/Invoice*[not (self::InvoiceDistributions)]

Code:
<Invoices>
    <Invoice>
        <Field1 />
	<Field2 />
	<Field3 />
	<InvoiceDistributions>
	    <InvoiceDistribution>
                <Field1 />
	        <Field2 />
	        <Field3 />
            </InvoiceDistribution>
	<InvoiceDistributions>
	<APHeaders>
	    <APHeader>
                <Field1 />
	        <Field2 />
	        <Field3 />
            </APHeader>
	</APHeaders>
    </Invoice>
</Invoices>

Any suggestions? Thanks.
 
Last edited:

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
I haven't done it this way, but this popped up in a Google search...

/rootNodeName/*[not(self::excludeChildName1)]

or for multiple exclusions...

/rootNodeName/*[not(self::excludeChildName1)][not(self::excludeChildName2)]
 

Mean MrMustard

Diamond Member
Jan 5, 2001
3,144
10
81
That didn't work. Here's the code:
Code:
Dim DocumentNumber as String = "I123"
Dim nsmgr As New XmlNamespaceManager(v_doc.NameTable)
nsmgr.AddNamespace("ns", "http://tempuri.org/InvoicesDataset.xsd")

Dim v_InvoiceNodes As XmlNodeList = v_doc.DocumentElement.SelectNodes(String.Format("ns:Invoices/ns:Invoice[ns:ivh_invoicenumber='{0}'][not(self::ns:InvoiceDistributions)]", DocumentNumber), nsmgr)

The [ns:ivh_invoicenumber='{0}'] works, so I have no idea why the other doesn't.
 

Markbnj

Elite Member <br>Moderator Emeritus
Moderator
Sep 16, 2005
15,682
14
81
www.markbetz.net
The other suggestion I had was just read the whole thing in, select out the nodes you don't want, and remove them. Seems clunky, I know.
 

ethebubbeth

Golden Member
May 2, 2003
1,740
5
91
XPath alone would be clunky to do what you are looking for, especially since .NET does not implement the entire XPath 2.0 specification. Have you looked into using XSLT and/or LINQ to XML to accomplish your goals?

Here is some LINQ to XML code to accomplish what I think you're looking for. I did it in C# but it should be easy to port to VB.NET.

Code:
string sourceXml = 
	@"<Invoices>
		<Invoice>
			<Field1>OMG</Field1>
			<Field2>WTF</Field2>
			<Field3>BBQ</Field3>
			<InvoiceDistributions>
				<InvoiceDistribution>
					<Field1>Blah</Field1>
					<Field2>Blah</Field2>
					<Field3>Blah</Field3>
				</InvoiceDistribution>
			</InvoiceDistributions>
			<APHeaders>
				<APHeader>
					<Field1>This</Field1>
					<Field2>one</Field2>
					<Field3>stays</Field3>
				</APHeader>
				<APHeader>
					<Field1>This</Field1>
					<Field2>one</Field2>
					<Field3>leaves</Field3>
				</APHeader>
				<APHeader>
					<Field1>This</Field1>
					<Field2>one also</Field2>
					<Field3>leaves</Field3>
				</APHeader>
			</APHeaders>
		</Invoice>
	</Invoices>";

XDocument doc = XDocument.Parse(sourceXml);

//removes all InvoiceDistributions elements in the document
doc.Descendants("InvoiceDistributions").Remove();

//specify a predicate to match specific APHeader elements.
//in this case, remove all the APHeader elements where Field3 is "leaves"
doc.Descendants("APHeaders").Descendants("APHeader").Where(e => e.Element("Field3").Value == "leaves").Remove();

string result = doc.ToString();

the result string ends up looking like this:
Code:
<Invoices>
  <Invoice>
    <Field1>OMG</Field1>
    <Field2>WTF</Field2>
    <Field3>BBQ</Field3>
    <APHeaders>
      <APHeader>
        <Field1>This</Field1>
        <Field2>one</Field2>
        <Field3>stays</Field3>
      </APHeader>
    </APHeaders>
  </Invoice>
</Invoices>
 
Last edited:

Mean MrMustard

Diamond Member
Jan 5, 2001
3,144
10
81
I tested that and it didn't work.

I tried...

doc.Descendants("InvoiceDistributions").Remove() and doc.Descendants("Invoice").Descendants("InvoiceDistributions").Remove()

Everything I've found says this is the way to do it so I don't know what I'm doing wrong.
 

Mean MrMustard

Diamond Member
Jan 5, 2001
3,144
10
81
I got it to work. The xml root had a namespace attribute. Once I removed it, it worked.

Thanks guys!