Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Friday, December 9, 2011

Using XPath to Select Nodes

XPath is a special query language the is specifically used for selecting nodes in an XML document. With these language, you don’t have to search for the entire tree of the XML nodes. You will learn the basics of these language and apply it to a program. The two methods used for selecting nodes using the XPath language are the XmlNode.SelectNodes() and the XmlNode.SelectSingleNode(). The SelectNodes() method returns a XmlNodeList which contains all the nodes that matches the XPath string. Consider the following XML document.




30
Male


25
Male


22
Female


27
Male


35
Male


Figure 1

Suppose you want to get the age of every person, the code for doing that is:

XmlDocument document = new XmlDocument();
document.Load("Persons.xml");

XmlNodeList nodes = document.DocumentElement.SelectNodes("/Persons/Person/Age");

foreach(XmlNode node in nodes)
{
textBoxResult.Text += node.InnerText + "\r\n";
}
After loading the document, we used the DocumentElement property which is of type XmlNode. We used the SelectNodes() method which accepts a string argument that contains the XPath query. The XPath query /Persons/Person/Age tells that get Age element that is a child of a Person element which is a child of the Persons element. All the matching nodes will then be returned as an XmlNodeList. We used a foreach loop to print each age in a text box. Figure 2 shows you some XPath operations that you can use to query specific nodes.

XPath Query Description
. Selects the current node.
.. Selects the parent of the current node.
* Selects all the child of the current node.
nodename Selects all child nodes specified by the name.
/ Selects the root node.
// Selects nodes from the current node that match the selection
expression no matter where they are.
//* Selects all elements in the document.
/element Selects the root element named element. Staring a path
with a / means you are using an absolute path to an element.
/element/* Selects all the child of the root element.
element/* Selects all the child nodes of a child element.
element/child Selects the child elements which are a child of a specified child
element of the current node.
//element Selects all elements with the specified name regardless of
where they are in the document.
element//child Selects all child elements of the parent regardless of
where they are inside the parent element.
@attribute Selects an attribute of the current node where attribute
is the name of the attribute.
//@attribute Selects all the attributes specified by its name regardless of
where they are in the document.
@* Selects all attribute of the current node.
element[i] Selects an element with the specified element name and the specified index.
text() Selects the text of all the child nodes of the current element.
//text() Selects text of every element in the document.
//element/text() Selects the text of all the matching elements.
//element[name='value'] Selects all elements with a child containing a specified value.
//element[@att='value'] Selects all elements with the specified attribute having the specified value.
Figure 2 – XPath Operations

For example, if you want to select the current node, then you will use the . operator.

XmlNode current = document.DocumentElement.SelectSingleNode(".");
Notice that we used the XmlNode.SelectSingleNode() method to only select 1 node. In case of multiple results, it will return the first matching node. The method accepted the XPath string as its argument. Passing “.” will result on returning the actual node that calls it. Also notice that the returned value is an XmlNode and not an XmlNodeList.

Suppose you want to select all the Person elements which are children of the Persons node. You can use the following XPath query.

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("/Persons/Person");
Notice that we started the query with a slash (/). This indicates that we are using an absolute path. We start fromt he Persons root node, then we look at all the Person nodes which is a direct child of the root node. If we are to get every age of each person, then we can use the following code.

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("/Persons/Person/Age");
You can also use a relative path where the searching starts from the current node. For example, you can query all the Person node from the DocumentElement root node using the following code:

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("Person");
or the age of every person usign the following code:

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("Person/Age");
Notice that we didn’t precede the XPath query with a / to indicate that we are using a relative path.

We can also query nodes regardless of where they are in the document. This is usefull when you wan’t to search the whole document and return all matching nodes. For example, if you want to query all the Gender nodes, even if you are starting from the root node, then you can use the following query:

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("//Gender");
We precede the element name with // to indicate that the whole document should be searched. The query will now return all the matching nodes wherever they are in the document. If you want to limit the area where the searching will be done, then you can specify the parent node or root node where the searching will start.

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("/Persons//Gender");
The above code will search for all Gender elements under the Persons node.

If you want to query specific elements, then you can use their index. The following queries the third child of the Persons element.

XmlNode personNodes =
document.DocumentElement.SelectSingleNode("/Persons/Person[3]");
Since we are querying a single node, we used the SelectSingleNode() method. The third Person element is represented by Person[3] where we used 3 as the index. Note that indices are base-1, so the counting starts with 1 and not 0 as opposed to arrays in C#.

If you want to search for all the person who has a gender of male, then you can use the following code.

XmlNodeList personNodes =
document.DocumentElement.SelectNodes("//Person[Gender='Male']");
Since we used a double slash (//), then the whole document will be searched. Inside the bracket, we specify the name of the child element and after that, we specified what the value should be. The value must be enclosed in single quotes if it represents a string.

When dealign with attributes of elements, you can use the @ operator followed by the name of the attribute. For example, the following prints all the name of every person.

XmlNodeList list = document.DocumentElement.SelectNodes(@"//Person/@name");

foreach (XmlNode node in list)
{
textBox1.Text += node.Value + "\r\n";
}
XPath is a big topic and only some basic components was discussed here. If you want to study everything about XPath, then you can go to the following link:
http://www.w3schools.com/xpath/default.asp

SelectSingleNode / SelectNodes

The selectSingleNode method returns a Node object for the first descendant node to match the specified pattern. The one parameter of this method is an XSL pattern query. If no match is made, it returns null. This method is similar to theselectNodes method, but returns only the first node to match the pattern rather than all of them.



// Load the document and set the root element.
XmlDocument doc = new XmlDocument();
doc.Load("bookstore.xml");
XmlNode root = doc.DocumentElement;

// Add the namespace.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("bk", "urn:newbooks-schema");

// Select and display the first node in which the author's 
// last name is Kingsolver.
XmlNode node = root.SelectSingleNode(
    "descendant::bk:book[bk:author/bk:last-name='Kingsolver']", nsmgr);
Console.WriteLine(node.InnerXml);
----------------
The primary means of reading and writing in C# 2.0 is done through the XmlDocument class. You can load most of your settings directly into the XmlDocument through the XmlReader it accepts.

Loading XML Directly

XmlDocument document = new XmlDocument();
document.LoadXml("");

Loading XML From a File

XmlDocument document = new XmlDocument();
document.Load(@"C:\Path\To\xmldoc.xml");
// Or using an XmlReader/XmlTextReader
XmlReader reader = XmlReader.Create(@"C:\Path\To\xmldoc.xml");
document.Load(reader);
I find the easiest/fastest way to read an XML document is by using XPath.

Reading an XML Document using XPath (Using XmlDocument which allows us to edit)

XmlDocument document = new XmlDocument();
document.LoadXml("");
// Select a single node
XmlNode node = document.SelectSingleNode("/People/Person[@Name = 'Nick']");
// Select a list of nodes
XmlNodeList nodes = document.SelectNodes("/People/Person");
If you need to work with XSD documents to validate an XML document you can use this.

Validating XML Documents against XSD Schemas

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd
XmlReader reader = XmlReader.Create(pathToXml, settings);
XmlDocument document = new XmlDocument();
try {
    document.Load(reader);
} catch (XmlSchemaValidationException ex) { Trace.WriteLine(ex.Message); }

Validating XML against XSD at each Node (UPDATE 1)

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidateType = ValidationType.Schema;
settings.Schemas.Add("", pathToXsd); // targetNamespace, pathToXsd
settings.ValidationEventHandler += new ValidationEventHandler(settings_ValidationEventHandler);
XmlReader reader = XmlReader.Create(pathToXml, settings);
while (reader.Read()) { }
private void settings_ValidationEventHandler(object sender, ValidationEventArgs args)
{
    // e.Message, e.Severity (warning, error), e.Error
    // or you can access the reader if you have access to it
    // reader.LineNumber, reader.LinePosition.. etc
}

Writing an XML Document (manually)

XmlWriter writer = XmlWriter.Create(pathToOutput);
writer.WriteStartDocument();
writer.WriteStartElement("People");

writer.WriteStartElement("Person");
writer.WriteAttributeString("Name", "Nick");
writer.WriteEndElement();

writer.WriteStartElement("Person");
writer.WriteStartAttribute("Name");
writer.WriteValue("Nick");
writer.WriteEndAttribute();
writer.WriteEndElement();

writer.WriteEndElement();
writer.WriteEndDocument();

writer.Flush();
(UPDATE 1)
In .NET 3.5, you use XDocument to perform similar tasks. The difference however is you have the advantage of performing Linq Queries to select the exact data you need. With the addition of object initializers you can create a query that even returns objects of your own definition right in the query itself.
    XDocument doc = XDocument.Load(pathToXml);
    List<Person> people = (from xnode in xdoc.Element("People").Elements("Person")
                       select new Person
                       {
                           Name = xnode.Attribute("Name").Value
                       }).ToList();
(UPDATE 2)
A nice way in .NET 3.5 is to use XDocument to create XML is below. This makes the code appear in a similar pattern to the desired output.
XDocument doc =
        new XDocument(
              new XDeclaration("1.0", Encoding.UTF8.HeaderName, String.Empty),
              new XComment("Xml Document"),
              new XElement("catalog",
                    new XElement("book",
                          new XAttribute("id", "bk001"),
                                new XElement("title", "Book Title")
                    )
              )
        );
creates


  < span=""> id="bk001"
    </span><span class="pln" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; vertical-align: baseline; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; ">Book Title</span><span class="tag" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; vertical-align: baseline; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; color: rgb(128, 0, 0); ">
  

All else fails, you can check out this MSDN article that has many examples that I've discussed here and more. http://msdn.microsoft.com/en-us/library/aa468556.aspx
-----------------------------------------