Using XPath Against A Node

D

Derek Hart

I bring in an xml file into vb.net by using xmlDoc.LoadXml(XMLString) - I
run xpath statements against the xml file to grab data from it, so I use, as
an example, //Vehicles/Vehicles/@make to get the make of the car. But then
I pass a specific node from xmlDoc into another function, not the whole
xmlDoc, just a node from it. And if I run an xpath against it, I have to
use .// (has a period at the beginning) so it does not grab info from the
parent. So I would have to use .//Vehicles/Vehicles/@make so it references
the current node. I would think that if I passed a node to a vb.net
function, I could simply use an xpath statement that would work off the
passed in node, and I could assume that the top part of that node was the
new parent. Is there a way to pass a node like this?

Derek
 
F

FishingScout

Derek,

I am not sure what you meant by: "and I could assume that the top part
of that node was the new parent."

Using "/" will search the document root node immediate descendants.
Using "./" will search the context node immediate descendants.
Using "// will search anywhere in the entire document.
Using ".//" will search anywhere in the context node descendants.
Similarly, you could use "descendant::".

If you know the absolute path of the data you are looking for, you can
simply skip the "//" or the "/". For example, if "Price" were an
attribute of "Model", which is an immediate descendant of Vehicles, you
could query for the price of a model using "Model/@Price". If you are
a high paid developer you could search for a specific model using
"Model[.='Cadillac']/@Price". You don't need to use ".//Model/@Price"
or "descendant::Model/@Price".

I don't know if this was helpful but .... I don't drive a Cadillac.
 
D

Derek Hart

So if I pass a node to a function, can I treat the top-most element as the
top parent? That is my question. Or is the entire xml document sent with
it. I do not want it to be possible to look at the whole document in the
function. I just want to pass in a specific node. Is it possible?

Derek


FishingScout said:
Derek,

I am not sure what you meant by: "and I could assume that the top part
of that node was the new parent."

Using "/" will search the document root node immediate descendants.
Using "./" will search the context node immediate descendants.
Using "// will search anywhere in the entire document.
Using ".//" will search anywhere in the context node descendants.
Similarly, you could use "descendant::".

If you know the absolute path of the data you are looking for, you can
simply skip the "//" or the "/". For example, if "Price" were an
attribute of "Model", which is an immediate descendant of Vehicles, you
could query for the price of a model using "Model/@Price". If you are
a high paid developer you could search for a specific model using
"Model[.='Cadillac']/@Price". You don't need to use ".//Model/@Price"
or "descendant::Model/@Price".

I don't know if this was helpful but .... I don't drive a Cadillac.


Derek said:
I bring in an xml file into vb.net by using xmlDoc.LoadXml(XMLString) - I
run xpath statements against the xml file to grab data from it, so I use,
as
an example, //Vehicles/Vehicles/@make to get the make of the car. But
then
I pass a specific node from xmlDoc into another function, not the whole
xmlDoc, just a node from it. And if I run an xpath against it, I have to
use .// (has a period at the beginning) so it does not grab info from the
parent. So I would have to use .//Vehicles/Vehicles/@make so it
references
the current node. I would think that if I passed a node to a vb.net
function, I could simply use an xpath statement that would work off the
passed in node, and I could assume that the top part of that node was the
new parent. Is there a way to pass a node like this?

Derek
 
M

Martin Honnen

Derek said:
So if I pass a node to a function, can I treat the top-most element as the
top parent? That is my question. Or is the entire xml document sent with
it. I do not want it to be possible to look at the whole document in the
function. I just want to pass in a specific node. Is it possible?

In the DOM an XmlNode always has an OwnerDocument property set to the
document that created the node. A node cannot exist without its owning
document as you can only create nodes with the factory methods of the
XmlDocument instance. A node does not need to have a ParentNode property
being non null however.

In terms of the XPath implementation with SelectSingleNode and
SelectNodes if the node is not inserted in the owning document then it
looks like the XPath / evaluates to the node itself. Thus if you do e.g.
Dim Xml_Doc as XmlDocument = New XmlDocument()
Dim SomeElement as XmlElement = Xml_Doc.CreateElement("whatever")
and pass that node around without inserting it into the document then
the node in terms of XPath and SelectNodes/SelectSingleNode does not
expose the document.
 
F

FishingScout

If there is some reason you don't want a method to access the document
from a node, it is possible to create a new xml document with only that
node and its descendants and pass it to a method. If the method
modifies the node you will need to do some work to replace the original
node with the modified node.

I don't know of any way to "protect" ancestor nodes within the DOM.

Derek said:
So if I pass a node to a function, can I treat the top-most element as the
top parent? That is my question. Or is the entire xml document sent with
it. I do not want it to be possible to look at the whole document in the
function. I just want to pass in a specific node. Is it possible?

Derek


FishingScout said:
Derek,

I am not sure what you meant by: "and I could assume that the top part
of that node was the new parent."

Using "/" will search the document root node immediate descendants.
Using "./" will search the context node immediate descendants.
Using "// will search anywhere in the entire document.
Using ".//" will search anywhere in the context node descendants.
Similarly, you could use "descendant::".

If you know the absolute path of the data you are looking for, you can
simply skip the "//" or the "/". For example, if "Price" were an
attribute of "Model", which is an immediate descendant of Vehicles, you
could query for the price of a model using "Model/@Price". If you are
a high paid developer you could search for a specific model using
"Model[.='Cadillac']/@Price". You don't need to use ".//Model/@Price"
or "descendant::Model/@Price".

I don't know if this was helpful but .... I don't drive a Cadillac.


Derek said:
I bring in an xml file into vb.net by using xmlDoc.LoadXml(XMLString) - I
run xpath statements against the xml file to grab data from it, so I use,
as
an example, //Vehicles/Vehicles/@make to get the make of the car. But
then
I pass a specific node from xmlDoc into another function, not the whole
xmlDoc, just a node from it. And if I run an xpath against it, I have to
use .// (has a period at the beginning) so it does not grab info from the
parent. So I would have to use .//Vehicles/Vehicles/@make so it
references
the current node. I would think that if I passed a node to a vb.net
function, I could simply use an xpath statement that would work off the
passed in node, and I could assume that the top part of that node was the
new parent. Is there a way to pass a node like this?

Derek
 
D

Derek Hart

Any sample code on breaking the node off the original xml document into its
own "smaller" document and node?

Derek

FishingScout said:
If there is some reason you don't want a method to access the document
from a node, it is possible to create a new xml document with only that
node and its descendants and pass it to a method. If the method
modifies the node you will need to do some work to replace the original
node with the modified node.

I don't know of any way to "protect" ancestor nodes within the DOM.

Derek said:
So if I pass a node to a function, can I treat the top-most element as
the
top parent? That is my question. Or is the entire xml document sent with
it. I do not want it to be possible to look at the whole document in the
function. I just want to pass in a specific node. Is it possible?

Derek


FishingScout said:
Derek,

I am not sure what you meant by: "and I could assume that the top part
of that node was the new parent."

Using "/" will search the document root node immediate descendants.
Using "./" will search the context node immediate descendants.
Using "// will search anywhere in the entire document.
Using ".//" will search anywhere in the context node descendants.
Similarly, you could use "descendant::".

If you know the absolute path of the data you are looking for, you can
simply skip the "//" or the "/". For example, if "Price" were an
attribute of "Model", which is an immediate descendant of Vehicles, you
could query for the price of a model using "Model/@Price". If you are
a high paid developer you could search for a specific model using
"Model[.='Cadillac']/@Price". You don't need to use ".//Model/@Price"
or "descendant::Model/@Price".

I don't know if this was helpful but .... I don't drive a Cadillac.


Derek Hart wrote:
I bring in an xml file into vb.net by using
xmlDoc.LoadXml(XMLString) - I
run xpath statements against the xml file to grab data from it, so I
use,
as
an example, //Vehicles/Vehicles/@make to get the make of the car. But
then
I pass a specific node from xmlDoc into another function, not the
whole
xmlDoc, just a node from it. And if I run an xpath against it, I have
to
use .// (has a period at the beginning) so it does not grab info from
the
parent. So I would have to use .//Vehicles/Vehicles/@make so it
references
the current node. I would think that if I passed a node to a vb.net
function, I could simply use an xpath statement that would work off
the
passed in node, and I could assume that the top part of that node was
the
new parent. Is there a way to pass a node like this?

Derek
 
F

FishingScout

Derek,

Here is an example. You will need to create a directory
"c:\xmlexample" or modify the paths in the code. After the code below
is the contents of an xml file that you should name "dealership.xml"
and save to the directory that you created. Even though changes can be
made to the XML DOM passed into the DoSomeXmlProcessing, they are not
available in the orignal XML DOM unless you copy them back into the
original XML DOM. You can verify this by commenting out the
....InnerXml = ...InnerXml line.

Create a VB.NET windows application.

Replace the contents of Form1.vb with this:

Imports System.Xml

Public Class Form1

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click

Dim WholeXmlDocument As New XmlDocument
WholeXmlDocument.Load("c:\xmlexample\dealership.xml")

' assume, for this example, the user wants
' all Chevy models displayed

Dim VehicleNode As XmlNode = _
WholeXmlDocument.SelectSingleNode("//Vehicles[@Make='Chevy']")

If Not VehicleNode Is Nothing Then

' Create a new document from the Vehicle
' node so the other developers cannot
' modify the actual dealership document

Dim ChevyXmlDoc As New XmlDocument
ChevyXmlDoc.AppendChild(ChevyXmlDoc.ImportNode( _
VehicleNode.Clone(), True))

Dim bDocumentModified As Boolean

bDocumentModified = DoSomeXmlProcessing(ChevyXmlDoc)

' Changes to the ChevyXmlDocument will only
' appear in the WholeXmlDocument if you copy
' the modified node back into the WholeXmlDocument
If bDocumentModified Then
VehicleNode.InnerXml = ChevyXmlDoc.Clone().InnerXml
End If

WholeXmlDocument.Save("c:\xmlexample\modified.xml")

End If

End Sub

Private Function DoSomeXmlProcessing(ByRef ModelXmlDocument _
As XmlDocument) As Boolean

Dim bDocumentWasModified As Boolean = False

' Uplander sales are down so give it a
' nice rebate
Dim UplanderNode As XmlNode = _
ModelXmlDocument.SelectSingleNode("//Model[.='Uplander']")

If Not UplanderNode Is Nothing Then

' 5000 should boost sales!

UplanderNode.Attributes("Rebate").InnerText = "5000"

bDocumentWasModified = True

End If

Return bDocumentWasModified

End Function
End Class


=========================
dealership.xml
=========================

<?xml version="1.0" encoding="utf-8"?>
<Dealership>
<Vehicles>
<Vehicles Make="Chevy">
<Model Price="49,000" Rebate="5000">Corvet</Model>
<Model Price="34,000" Rebate="2500">Tahoe</Model>
<Model Price="21,000" Rebate="0">Uplander</Model>
</Vehicles>
<Vehicles Make="Bavarian Motor Works">
<Model Price="85,000" Rebate="0">740i</Model>
<Model Price="45,000" Rebate="0">530i</Model>
<Model Price="35,000" Rebate="0">325i</Model>
</Vehicles>
</Vehicles>
</Dealership>



Derek said:
Any sample code on breaking the node off the original xml document into its
own "smaller" document and node?

Derek

FishingScout said:
If there is some reason you don't want a method to access the document
from a node, it is possible to create a new xml document with only that
node and its descendants and pass it to a method. If the method
modifies the node you will need to do some work to replace the original
node with the modified node.

I don't know of any way to "protect" ancestor nodes within the DOM.

Derek said:
So if I pass a node to a function, can I treat the top-most element as
the
top parent? That is my question. Or is the entire xml document sent with
it. I do not want it to be possible to look at the whole document in the
function. I just want to pass in a specific node. Is it possible?

Derek


Derek,

I am not sure what you meant by: "and I could assume that the top part
of that node was the new parent."

Using "/" will search the document root node immediate descendants.
Using "./" will search the context node immediate descendants.
Using "// will search anywhere in the entire document.
Using ".//" will search anywhere in the context node descendants.
Similarly, you could use "descendant::".

If you know the absolute path of the data you are looking for, you can
simply skip the "//" or the "/". For example, if "Price" were an
attribute of "Model", which is an immediate descendant of Vehicles, you
could query for the price of a model using "Model/@Price". If you are
a high paid developer you could search for a specific model using
"Model[.='Cadillac']/@Price". You don't need to use ".//Model/@Price"
or "descendant::Model/@Price".

I don't know if this was helpful but .... I don't drive a Cadillac.


Derek Hart wrote:
I bring in an xml file into vb.net by using
xmlDoc.LoadXml(XMLString) - I
run xpath statements against the xml file to grab data from it, so I
use,
as
an example, //Vehicles/Vehicles/@make to get the make of the car. But
then
I pass a specific node from xmlDoc into another function, not the
whole
xmlDoc, just a node from it. And if I run an xpath against it, I have
to
use .// (has a period at the beginning) so it does not grab info from
the
parent. So I would have to use .//Vehicles/Vehicles/@make so it
references
the current node. I would think that if I passed a node to a vb.net
function, I could simply use an xpath statement that would work off
the
passed in node, and I could assume that the top part of that node was
the
new parent. Is there a way to pass a node like this?

Derek
 

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

Top