XML in the Microsoft .NET Framework

By: John Ray Thomas

Abstract: XML is used throughout the Microsoft .NET Framework. This tutorial provides a basic understanding of what is provided and how you can use it in your applications.

John Ray Thomas (JT) is a product manager for the RAD Business Unit at Borland. He is an avid game and graphics programmer and is a big fan of alternative operating systems. JT is also a student of business and engineering at San Jose State University and lives in Santa Cruz with his wife, Jessica, and baby daughter, Izabella.

jthomas@borland.com

XML in the Microsoft .NET Framework

Borland Developers Conference 2004

Authored by Robert Love

 

Introduction.. 2

Reading an XML Document.. 2

Reading with XmlReader Descendants. 2

Reading with XmlDocument 5

Reading with XmlDataDocument 7

Reading with the XPathNavigator Class. 7

Writing an Xml Document.. 9

Writing with XmlWriter 9

Writing with XmlDocument 10

Writing with XmlDataDocument 11

Xml Object Serialization.. 12

What is Object Serialization?. 12

How to use XmlSerializer 12

Controlling Serialization through Attributes. 14

Performing XSLT Transformations.. 15

Tools.. 15

Appendix.. 17

Training Example Xml Document 17

XPath. 17

Getting Started with XPath. 17

Relative Paths. 18

Path Wildcards. 19

Current and Prior Node. 20

XPath Axes. 21

XPath Predicates. 22

Accessing Attributes. 23

XPath Expression. 24

XPath Functions. 26

XSLT.. 29

XSLT Declaration. 29

Matching and Applying Templates. 29

value-of element 31

for-each element 32

Extending Templates. 33

Recommended References.. 35

Contact Information.. 36

 


Introduction

XML has become an industry standard and is universally accepted. It is used in a number of different ways. The .NET framework is no exception. There are many different and unique ways XML is used in the .NET Framework. This document will attempt to show some common XML tasks, and how to address these using the classes in the framework.

 

Reading an XML Document

There are many built-in ways to read an Xml Document, each with its own benefits. Understanding these benefits and associated drawbacks, will help you in determining which class is the correct one to use.

 

Some of the ways to read an Xml Document in the .NET Framework

  • The Abstract XmlReader Class Descendants
    • XmlTextReader
    • XmlValidatingReader
    • XmlNodeReader
  • The XmlDocument Class and Descendants
    • XmlDataDocument
  • The XPathDocument Class
  • The ReadXml Method of Dataset
  • Classes in System.Xml.Serialization Namespace

 

Reading with XmlReader Descendants

 

XmlReader provides an abstract model for accessing Xml Data. XmlReader descendants provide a Read Only, Uni-Directional view of an Xml Document. They also provide an easy interface to walk an Xml document from beginning to end.

 

There are three built in XmlReader Descendants

Class

Purpose

XmlTextReader

Read an Xml document stream or file with out validating to a schema or a Document Type Definition (DTD).

XmlNodeReader

Read from an XmlNode descendant such as XmlDocument which is the .NET DOM Implementation.

XmlValidatingReader

Read an Xml Document Stream or File, allowing it to be validated to a schema or a Document Type Definition (DTD).

 

When using XmlTextReader or XmlValidatingReader you can keep your memory footprint lower, since the entire document does not need to be loaded in memory. This makes these classes very useful for large files where the file size is greater than the available memory. The XmlNodeReader does not have this benefit as it is reading from an XmlNode descendant. Typically this XmlNode is an XmlDocument which already has loaded the entire document in to memory.

 

The XmlReader provides several methods to assist you in walking through all parts of an Xml Document. Each of these is very well documented in the .NET SDK Help File. I dont intend to cover all of these, only the essential methods needed to do basic parsing.

 

When reading with an XmlReader each part of the Xml document is separated in to a node. For example, the following XML structure would be broken into five different nodes.

 

<example><name>value</name></example>

Document Part

Node Type

<example>

XmlNodeType.Element

<name>

XmlNodeType.Element

Value

XmlNodeType.Text

</name>

XmlNodeType.EndElement

</example>

XmlNodeType.EndElement

 

When constructing an instance of an XmlReader descendant you specify the source of the Xml Document. In the case of XmlTextReader you have many different overloaded constructors allowing you to specify many different types of sources, such as the file name or stream that contains the Xml document. The XmlNodeReader needs an XmlNode as the source document; typically this is an XmlDocument class. The overloaded constructors for the XmlValidatingReader can accept a stream, string, or another XmlReader descendant as parameters.

 

After the XmlReader descendant instance has been constructed you call the Read method to read in the first node. Then using one of the many XmlReader properties you can access the details regarding that node, such as NodeType, Name and Value. You can then continue to call the Read method to loop through the entire document. When the Read method returns false you have reached the end of the document.

 

The following uses an XmlTextReader to loop through a specified file and add all the element names in the Xml document to a ListBox.

Var

xr : XmlTextReader;

begin

xr := XmlTextReader.Create(txtFileName.Text);

while xr.Read Do

begin

if xr.NodeType = XmlNodeType.Element then

ListBox1.Items.Add(xr.Name);

end;

end;

 

 

 

The following uses an XmlTextReader to loop through a specified file, adding the text values to the list box where the element name is PHONE. Notice how you must call Read again to get the node that contains the text value of the phone number.

Delphi for .NET

Var

xr : XmlTextReader;

begin

xr := XmlTextReader.Create(txtFileName.Text);

while xr.Read Do

begin

if (xr.NodeType = XmlNodeType.Element) and (xr.Name = 'PHONE') then

begin

if Not xr.Read then Break;

ListBox1.Items.Add(xr.Value);

end;

end;

 

The XmlValidatingReader provides an event named ValidationEventHandler. This event is used to notify your application of a validation error that may occur when looping through the document.

 

The following example demonstrates the use of the ValidationEventHandler to validate the specified document.

/// Validation Event

procedure TMyWinForm.ValidationEvent(sender: Tobject;

args: ValidationEventArgs);

begin

isValid := False;

ValidErrorMsg := args.Message;

end;

 

// Button Click that opens document and validates it.

procedure TMyWinForm.ValidateBtn_Click(sender: System.Object; e: System.EventArgs);

var

tr : XmlTextReader;

vr : XmlValidatingReader;

begin

// isValid is Declared in the Class, default to being valid

isValid := True;

 

// Construct the XmlTextReader

tr := XmlTextReader.Create(txtFileName.Text);

 

// Construct the Validating Reader Passing the XmlTextReader

vr := XmlValidatingReader.Create(tr);

 

// Attach Event Handler to be Notified if error occurs.

Include(vr.ValidationEventHandler,ValidationEvent);

 

// Read through all nodes of the file

while vr.Read do;

 

// Check if isValid was set to true in the event and what message occurred.

if Not isValid then

MessageBox.Show(ValidErrorMsg)

else

MessageBox.Show('Document is Valid');

end;

 

 

Reading with XmlDocument

 

The XmlDocument class implements the Xml DOM specification. Since DOM is a W3C standardized API, much of the functionality is similar between different implementations. When an XmlDocument is constructed the entire Xml document is parsed and stored as a tree in memory. This gives you bi-directional navigation, with the ability to make changes to the XmlDocument in memory. Since the entire document is in memory the class is not practical to use with large Xml documents.

 

After an XmlDocument is constructed you must call Load, or LoadXML to specify the source document. The document source can be one of many different things including a String, File, Stream, or XmlReader. No Validating is done on the document, if you need the document validated you should use an XmlValidatingReader to retrieve the source document. The XmlDocument provides many methods to navigate and work with an Xml Document. The .NET SDK Documentation is an excellent source of information on all of the methods that are provided by the XmlDocument Class. This paper will attempt to show how to use some of these methods.

 

The key class when dealing with an XmlDocument is an XmlNode. An XmlNode can have any number of attributes and child XmlNodes. In fact, XmlDocument is a descendant of XmlNode with a few additional methods and properties, such as those used to read and write an Xml document.

 

The XmlNode Child Nodes can be accessed through the following properties:

ChildNodes, FirstChild, LastNode, PreviousSibling, NextSibling. The ChildNodes contain the array of all nodes which can be looped through, using either a for loop or a while loop.

There are also two other key methods that allow you to execute an XPath Statement and return a single node, or set of nodes: SelectSingleNode and SelectNodes. These methods are very useful as they prevent you from having to walk the document tree to get what you are after. Instead, you can specify an XPath Query that will return the XmlNode(s) you are after.

 

The following example uses a FOR loop (via Index) to navigate all of the children of the root element adding their names to a ListBox.

Var

XmlDoc : XmlDocument;

I : Integer;

RootNode : XmlNode;

begin

XmlDoc := XmlDocument.Create;

XmlDoc.Load(txtFileName.Text);

// Select the Root Node

RootNode := XmlDoc.SelectSingleNode('/*');

// Loop Through all of the Child Nodes of the Root and Display their names

For I := 0 to RootNode.ChildNodes.Count -1 do

ListBox1.Items.Add(RootNode.ChildNodes.Item(I).Name);

end;

 

The following example uses a FOR loop (via Delphi 9 in Construct) to navigate all of the children of the root element adding their names to a ListBox.

Var

XmlDoc : XmlDocument;

I : Integer;

RootNode : XmlNode;

ChildNode : XmlNode;

begin

XmlDoc := XmlDocument.Create;

XmlDoc.Load(txtFileName.Text);

// Select the Root Node

RootNode := XmlDoc.SelectSingleNode('/*');

// Loop Through all of the Child Nodes of the Root and Display their names

For ChildNode in RootNode.ChildNodes do

ListBox1.Items.Add(ChildNode.Name);

end;

 


 

 

The following example uses a WHILE loop to navigate all of the children of the root element, adding their names to a ListBox.

Var

XmlDoc : XmlDocument;

RootNode : XmlNode;

ChildNode : XmlNode;

begin

XmlDoc := XmlDocument.Create;

XmlDoc.Load(txtFileName.Text);

// Select the Root Node

RootNode := XmlDoc.SelectSingleNode('/*');

ChildNode := RootNode.FirstChild;

// Loop Through all of the Child Nodes of the Root and Display their names

While Assigned(ChildNode) do

begin

ListBox1.Items.Add(ChildNode.Name);

ChildNode := ChildNode.NextSibling;

end;

end;

 

Reading with XmlDataDocument

 

The XmlDataDocument descends from XmlDocument and is enhanced to load and save data to and from a Dataset. This can be a useful method to data bind visual controls to an Xml Document.

 

In order to load an Xml Document with XmlDataDocument you will need a schema that describes the Document. This document tells the XmlDataDocument how to layout the tables and rows in the DataSet class.

 

First you must call the ReadXmlSchema method of the XmlDataDocument Class to pass the schema of the document. Then you can call Load to load the document desired.

 

Then you can use the DataSet Property to work with the Xml Document as a DataSet, or you can work with XmlDataDocument just like you would with XmlDocument.

 

Reading with the XPathNavigator Class

 

The XPathNavigator class is one of my favorite methods for reading Xml Documents. Used in combination with the XPathDocument Class, It provides a fast and efficient way to query data stored in an Xml Document. The only draw back to using XPathDocument is that it is read only. However, with .NET 2.0 XPathDocument will add the ability to modify the document. Microsoft has already started to recommend using XPathDocument over XmlDocument where possible. If you are unfamiliar with XPath, I suggest you skip to the appendix section on XPath found on Page 18.

 

The most important method on the XPathDocument class is CreateNavigator which creates the XPathNavigator that allows you navigate the document. There are many ways to use the XPath Navigator. In the Example below I show how to use the Select method to create an XpathNodeIterator to loop through the results of an XPath Query.

var

xNavDoc : XPathDocument;

xNav : XPathNavigator;

Nodes : XPathNodeIterator;

begin

xNavDoc := XPathDocument.Create('C:DEMOCLIENTLIST.XML');

xNav := xNavDoc.CreateNavigator;

Nodes := xNav.Select('/CLIENTLIST/CLIENT/PHONE');

while Nodes.MoveNext do

begin

ListBox1.Items.Add(Nodes.Current.Value);

end;

end;

 


Writing an Xml Document

 

There are many built-in ways to write an Xml Document, each has its own benefits. Understanding these benefits and associated drawbacks, will help you in determining which class is the correct one to use.

 

Some of the ways to write an Xml Document in the .NET Framework

  • The Abstract XmlWriter Class Descendant
    • XmlTextWriter
  • The XmlDocument Class and Descendants
    • XmlDataDocument
  • The WriteXml Method of DataSet
  • Classes in System.Xml.Serialization Namespace

 

 

Writing with XmlWriter

 

The XmlWriter is an abstract class. The framework provides only one decendant the XmlTextWriter. The XmlWriter provides the basic functionality required to create Xml. You can create both Xml documents and document fragements using the XmlWriter. It is possible to create non-well formed Xml Documents with XmlWriter so care must be taken to verify that your output is well formed. The XmlWriter is a very fast and efficent method to output Xml. For the most part, the other Framework classes that create Xml are output through the XmlTextWriter. I personally find the XmlWriter to be the easiest way to write Xml Documents.

 

Using an XmlWriter to produce the XML for an RSS feed.

Delphi for .NET

function TWinForm3.BasicRSSFeed: string;

var

XW : XmlTextWriter;

SW : StringWriter;

begin

SW := StringWriter.Create;

XW := XmlTextWriter.Create(SW);

 

XW.WriteStartDocument;

XW.WriteStartElement('rss');

XW.WriteAttributeString('version','2.0');

XW.WriteStartElement('channel');

XW.WriteElementString('title','Title of My Feed');

XW.WriteElementString('link','http://linktomyfeed/');

XW.WriteElementString('description','Description of my Feed');

XW.WriteStartElement('item');

XW.WriteElementString('title','Item Title');

XW.WriteElementString('description','This is the content');

XW.WriteElementString('pubdate',DateTime.Now.ToUniversalTime.ToString('r'));

XW.WriteEndElement; //item

XW.WriteEndElement; // channel

XW.WriteEndElement; // rss

XW.WriteEndDocument;

XW.Close;

SW.Close;

result := SW.ToString;

end;

 

Writing with XmlDocument

 

XmlDocument can be used to write Xml Documents, as well as read them.

 

<?xml version="1.0" encoding="utf-16"?>
<events>
<event name="BorCon">
<location>San Jose</location>
</event>
</events>

 

The following function demostrates how to create the above document using XmlDocument.

 

function CreateSampleDocument : String;

var

DOM : XmlDocument;

RootNode : XmlNode;

EventNode : XmlNode;

LocationNode : XmlNode;

AttrNode : XmlAttribute;

writer : StringWriter;

begin

DOM := XmlDocument.Create;

// Create Root Node

RootNode := DOM.CreateElement('events');

// Append Root Model

DOM.AppendChild(RootNode);

 

// Create Event Node

// Note: This has the same effect as calling CreateElement

// CreateNode can be used to create all the different element types

EventNode := DOM.CreateNode(XmlNodeType.Element,'event','');

 

// Create and Add Attribute to EventNode

AttrNode := DOM.CreateAttribute('name');

AttrNode.Value := 'BorCon';

EventNode.Attributes.Append(AttrNode);

 

// Create and Add Location to Event Node

LocationNode := DOM.CreateElement('location');

LocationNode.InnerText := 'San Jose';

EventNode.AppendChild(LocationNode);

 

// Append Event Node to Root Node

RootNode.AppendChild(EventNode);

 

// Create StringWriter for Save

writer := StringWriter.Create;

// Save is overloaded and can take one of the following as params

// XmlWriter, TextWriter, FileName, Stream

DOM.Save(writer);

 

// output String Result

result := writer.ToString;

end;

Writing with XmlDataDocument

One of the common problems you may face in development is exporting relation data into a text based format, such as XML.    This can be accomplished using XmlDataDocument which represents your relational data as an XmlDocument.  The XmlDataDocument can then be used as a source to an XSLT Transformation to produce the output document.
   

  1. Create a Dataset with the contents from your Database.
  2. Create an XmlDataDocument passing the DataSet that is filled with your database content.
  3. Create an XslTransform and Load the XSLT document that will transform your dataset XML document into the resulting feed format.
  4. Call Transform on the XslTranform which will place your output into one of many formats. In the example below I am using a StringWriter.

 

Var
XDD : XmlDataDocument;
SW : StringWriter;
XT : XslTransform;

DS : DataSet;
begin
BdpDataAdapter1.Fill(DS);
XDD := XmlDataDocument.Create(DS);
SW := StringWriter.Create();
XT := XslTransform.Create;
XT.Load('C:DEMOSTRANSFORM.XSL');
XT.Transform(XDD,nil,SW);
TextBox1.Text := SW.ToString;
end;

 


Xml Object Serialization

What is Object Serialization?

 

Serialization allows you to convert an object to a persistent medium, such as a file. Then at any given time the object can be de-serialized and loaded with values from the previously persistent state. One common application of serialization is with web services, allowing your web methods to return objects. The returning objects will be serialized as Xml, allowing them to be transmitted over the wire.

 

The .Net Framework provides everything you need to serialize an object in Xml Format.

 

There are some restrictions on Xml serialization.

  • Only public properties and fields can be serialized.
  • A class must have a default constructor to be serialized.
  • Methods can not be serialized.

 

How to use XmlSerializer

 

Serialization is handled through the XmlSerializer class. When constructing an instance of the XmlSerializer you must pass the typeof the class you wish to serialize.

Serialization is done with call to XmlSerializers Serialize method, and De-serialization is done with a call to the Deserialize method.

 

Basic Serialization Example

Type

// Class to Serialize

CustomerClass = class(TObject)

public

FirstName : String;

LastName : String;

end;

 

procedure Serialize;

var

Customer : CustomerClass;

XS : XmlSerializer;

SW : StringWriter;

begin

// Create an instance of CustomerClass and populate it with data

Customer := CustomerClass.Create;

Customer.FirstName := 'John';

Customer.LastName := 'Doe';

 

// Create an instance of XmlSerializer passing the CustomerClass Type

XS := XmlSerializer.Create(TypeOf(CustomerClass));

 

// Create a TextWriter Decendant to store Serialized Object

SW := StringWriter.Create;

 

// Serialize the Object to the TextWriter

XS.Serialize(SW,Customer);

// Show Serialized Object in a Message Box

MessageBox.Show(SW.ToString);

end;

 

 

Basic De-serialization Example

Type

// Class to Serialize

CustomerClass = class(TObject)

public

FirstName : String;

LastName : String;

end;

 

procedure DeSerialize(DocumentStr : String);

var

Customer : CustomerClass;

XS : XmlSerializer;

SR : StringReader;

begin

// Create an instance of XmlSerializer passing the CustomerClass Type

XS := XmlSerializer.Create(TypeOf(CustomerClass));

 

// Create a TextReader or Stream Decendant and load the serialized data into it.

SR := StringReader.Create(DocumentStr);

 

// Deserialize the TextReader/Stream Decendant and Type Cast it to CustomerClass

Customer := CustomerClass(XS.Deserialize(SR));

 

// Display a Message Box to show Customer Information

MessageBox.Show(Customer.FirstName + ' ' + Customer.LastName);

 

end;

 


Controlling Serialization through Attributes

 

There are several different attributes that can be applied to your class to control how it is serialized to Xml.

 

Example of Renaming the Xml Document Root Element and Sub Element Names

[XmlRoot(ElementName = 'customer')]

CustomerClass = class(TObject)

public

[XmlElement(ElementName = 'FNAME')]

FirstName : String;

[XmlElement(ElementName = 'LNAME')]

LastName : String;

end;

Resulting File

<?xml version="1.0" encoding="utf-16"?>

<customer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<FNAME>John</FNAME>

<LNAME>Doe</LNAME>

</customer>

 

Example using XmlAttribute and Embeded Classes

 

[XmlRoot(ElementName = 'customer')]

CustomerClass = class(TObject)

public

type

NameClass = Class(TObject)

public

[XmlAttribute('First')]

FirstName : String;

[XmlAttribute('Last')]

LastName : String;

end;

public

[XmlElement('name')]

Name : NameClass;

end;

Resulting File

<?xml version="1.0" encoding="utf-16"?>

<customer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<name First="John" Last="Doe" />

</customer>

 

Performing XSLT Transformations

 

The .Net Framework provides the necessary classes to perform XSLT. The XslTranform class provides all of the functionality required. There are two key methods that need to be called for every transformation. First you must call XslTransform.Load to load the stylesheet that will be used in the transformation. The second is XslTransform.Transform which will perform the transformation on a specified Xml Source. Both methods are overloaded and have many different signatures, please refer to the SDK Documentation for details on each overload.

 

Var
XDD : XmlDataDocument;
SW : StringWriter;
XT : XslTransform;

DS : DataSet;
begin
BdpDataAdapter1.Fill(DS);
XDD := XmlDataDocument.Create(DS);
SW := StringWriter.Create();
XT := XslTransform.Create;

// Load The transformation
XT.Load('C:DEMOSTRANSFORM.XSL');

// Perform Transformation
XT.Transform(XDD,nil,SW);
TextBox1.Text := SW.ToString;
end;

 

Tools

The .Net framework provides a command line utility (XSD.EXE) that is very useful for dealing with serialization and schemas.

 

XSD.EXE  The Xml Schema Definition Tool

 

The XSD tool can do the following

7        Generating Schema Documents, from one or more Xml Instance Documents

7        Creating a Serialized Class from a Schema

7        Creating a typed DataSet Class from a Schema

7        Convert a Compiled Serialized Class in an Assembly in to XSD

 

Petr Vones a repected member of the Delphi Community has created a Delphi open tool that wraps the functionality found in XSD. The Open Tool also contains several other useful XML Tools. This tool makes it easy to create Delphi versions of the Classes.

 

This tool can be downloaded here:

http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=21094

 

 

 


Appendix

 

Training Example Xml Document

 

The following is the Xml Document that is used in the examples found in the Appendix.

 

Training.xml

<?xml version=1.0 encoding=ISO-8859-1 standalone=yes?>

<training>

<class>

<name number=401> Introduction to XML</name>

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<presenter>Robert Love</presenter>

<event name=BorCon/>

</class>

<class>

<name number=402> Introduction to .NET</name>

<subject>Borland will really show you how to do .NET</subject>

<presenter>Al Borland</presenter>

<event name=BorCon/>

</class>

</training>

 

XPath

 

XPath is a syntax used to query an XML document tree. XPath gets its name due to the path notation that it uses to describe the XML tree. XPath is a critical stepping-stone if you desire to learn XSLT. It also provides a quick way you can query an XML document and get either a node set, or a single value result.

 

 

Getting Started with XPath

 

XPath uses a path like syntax. The path delimiter is /" like you would find in a Linux based system. Each node in the tree can be compared to a directory on your file system. Most of the examples in this section will be using the original training.xml document. The results of each query will display the raw XML of each of the nodes returned.

 

Say we want to get a list of presenters in our document. We can access that list by specifying the path to the presenter.

Query

 

 

/training/class/presenter

 

Result

 

 

<presenter>Robert Love</presenter>

<presenter>Al Borland</presenter>

 

 

 

Stepping back one level and returning a list of classes will not just return the class but all child elements.

 

Query

 

 

/training/class

 

Result

 

 

<class>

<name number="401">Introduction to XML</name>

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<presenter>Robert Love</presenter>

<event name="BorCon"/>

</class>

<class>

<name number="402">Introduction to .NET</name>

<subject>Borland will really show you how to do .NET</subject>

<presenter>Al Borland</presenter>

<event name="BorCon"/>

</class>

 

 

Relative Paths

 

Currently all Xpath statements we have explored have started with a single / this is considered an absolute path. If you start directly with a name such as training/class/subject it is applied to a current context node. In all of the prior examples the context has been the implied document node. When we introduce XPath predicates and XSLT you will see practical use of relative paths.

You also have an option that allows you to search for nodes with-in a document. Using // you can search for all nodes, regardless of location, that match the following.

 

If you want to see all of the nodes with name of subject this could be done with.

Query

 

 

//subject

 

Result

 

 

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<subject>Borland will really show you how to do .NET</subject>

 

 

Path Wildcards

 

You can also use * as a wildcard inside of your path.

 

The following example uses the wild card twice. It will return all subject nodes that are in the 3rd tree level.

Query

 

 

/*/*/subject

 

Result

 

 

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<subject>Borland will really show you how to do .NET</subject>

 

 


Current and Prior Node

Just like paths you can use . and .. to represent the current and prior nodes.

The following example shows how to use . to get the current node.

Although this example is not very practical, it is only meant to introduce you to the fact that . can be used.

Query

 

/training/*/.

Result

 

<class>

<name number="401">Introduction to XML</name>

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<presenter>Robert Love</presenter>

<event name="BorCon"/>

</class>

<class>

<name number="402">Introduction to .NET</name>

<subject>Borland will really show you how to do .NET</subject>

<presenter>Al Borland</presenter>

<event name="BorCon"/>

</class>

 

The following example shows how to use .. to get the previous node.

Although this example is not very practical, it is only meant to introduce you to the fact that .. can be used.

Query

 

/training/*/..

Result

 

<training>

<class>

<name number="401">Introduction to XML</name>

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<presenter>Robert Love</presenter>

<event name="BorCon"/>

</class>

<class>

<name number="402">Introduction to .NET</name>

<subject>Borland will really show you how to do .NET</subject>

<presenter>Al Borland</presenter>

<event name="BorCon"/>

</class>

</training>

 


 

 

XPath Axes

 

Current and Prior nodes can also be obtained through an axis type statement. An axis allows you to specify what the current node will query. To specify an axis, the location path is prefixed with the axis.

 

 

This can be done with the following syntax:

 

axis::path

 

Following shows two ways to get current and prior nodes.

Axis Syntax

. and .. Syntax

/training/*/self::*

/training/*/.

/training/*/parent::*

/training/*/..

 

So basically . is a shortcut for the self axis, and .. is a shortcut for the parent axis.

 

There are several other axes that you can use in an XPath statement.

Axis

Description

Many descriptions taken directly from the XPath Specification

Self

Identifies the context node.

Child

Contains the children of the context node

This is the default axis. If you dont specify an axis child is implied.

Parent

Contains the parent of the context node if there is one.

descendant

Contains the descendants of the context node; a descendant is a child or a child of a child and so on; thus the descendant axis never contains attribute or namespace nodes.

descendant-or-self

Contains the context node and the descendants of the context node.

Ancestor

Contains the ancestors of the context node; the ancestors of the context node consist of the parent of context node and the parent's parent and so on; thus, the ancestor axis will always include the root node, unless the context node is the root node.

Ancestor-or-self

Contains all nodes in the same document as the context node that are after the context node in document order, excluding any descendants and excluding attribute nodes and namespace nodes.

following

Contains all nodes in the same document as the context node that are after the context node in document order, excluding any descendants and excluding attribute nodes and namespace nodes.

following-sibling

Contains all the following siblings of the context node; if the context node is an attribute node or namespace node.

preceding

Contains all nodes in the same document as the context node that are before the context node in document order, excluding any ancestors and excluding attribute nodes and namespace nodes.

preceding-sibling

Contains all the preceding siblings of the context node; if the context node is an attribute node or namespace node, the preceding-sibling axis is empty.

attribute

Contains the attributes of the context node; the axis will be empty unless the context node is an element.

namespace

Contains the namespace nodes of the context node; the axis will be empty unless the context node is an element.

 

 

XPath Predicates

 

The path statement will only take you so far. Sometimes you need to select which part of the list you are looking for. Using the [] you can include a statement to limit the result set, this is called Predicate.

 

In the query below we look at the presenter name and only return the class that matches that presenters name.

Query

 

 

/training/class[presenter="Robert Love"]

 

Result

 

 

<class>

<name number="401">Introduction to XML</name>

<subject>XML, DTD, XSD, DOM, XPATH, and XSLT.</subject>

<presenter>Robert Love</presenter>

<event name="BorCon"/>

</class>

 

 


Accessing Attributes

To access an attribute you must prefixing its name with a @.

 

In the following example, we are requesting a list of classes where number attribute is the value of 402.

Query

 

 

/training/class[name/@number="402"]

 

Result

 

 

<class>

<name number="402">Introduction to .NET</name>

<subject>Borland will really show you how to do .NET</subject>

<presenter>Al Borland</presenter>

<event name="BorCon"/>

</class>

 

 


XPath Expression

 

As introduced, with predicates you can compare equality of a given value. XPath provides many other options to build XPath expressions. These can be broken down in to Equality, Relational, Boolean and Numeric Expressions. Expressions can be applied to both single node values as well as node-sets.

 

Single Node Sets

 

 

Equality Expressions

Operator

Description

Example

Result

=

Equal

/*/class[1]/*/@number = 401

true

!=

Not Equal

/*/class[1]/*/@number != 401

false

 

Relational Expressions

If used inside of an XML document it must be escaped with the correct entity reference.

Operator

Description

Example

Result

&lt;

Less Than

/*/class[1]/*/@number &lt;= 401

false

<=

&lt;=

Less Than or Equal

/*/class[1]/*/@number &lt;= 401

true

&gt;

Greater Than

/*/class[1]/*/@number &gt; 401

false

>=

&gt;=

Greater Than or Equal

/*/class[1]/*/@number &gt;= 401

true

 

Boolean Expressions

Operator

Description

Example

Result

and

Both values are true

(/*/class[1]/*/@number &gt; 100) and (/*/class[1]/*/@number &lt; 300)

false

or

One of the values is true

(/*/class[1]/*/@number &gt; 100) or (/*/class[1]/*/@number &lt; 300)

true

 

 

Numeric Expressions

Operator

Description

Example

Result

+

Addition

/*/class[1]/*/@number + 100

501

-

Subtraction

/*/class[1]/*/@number  100

301

*

Multiplication

/*/class[1]/*/@number * 2

802

div

Division

/*/class[1]/*/@number div 2

200.5

mod

Modulus

/*/class[1]/*/@number mod 2

1

 

 

 

 

 

 

 

 

Multiple Node Sets

Note:

Query

//@Number

Result

number="401"

number="402"

 

Special rules apply when multiple values are returned in a node set.

When comparing equality between a given value and a node set, the result is true if any value in the node set matches the given value.

When comparing in-equality between a given value and node set, the result is true if any value in the node set does not match the given value. This means that it possible for node set to be both equal and not equal to the given value.

 

Equality Expressions

Operator

Description

Example

Result

=

Equal

@number = 401

true

!=

Not Equal

@number != 401

true

 

Relational Expressions

If used inside of an XML document it must be escaped with the correct entity reference.

Operator

Description

Example

Result

&lt;

Less Than

//@number &lt; 401

false

<=

&lt;=

Less Than or Equal

//@number &lt;= 401

true

&gt;

Greater Than

//@number &gt; 401

true

>=

&gt;=

Greater Than or Equal

//@number &gt;= 401

true

 

Boolean Expressions

Operator

Description

Example

Result

and

Both values are true

(//@number &gt; 200) and

(//@number &lt; 400)

false

or

One of the values is true

(//@number &gt; 200) or

(//@number &lt; 400)

true

 

Numeric Expressions

If a node set is returned, only the first value in the node set is used.

Operator

Description

Example

Result

+

Addition

//@number + 100

501

-

Subtraction

//@number - 100

301

*

Multiplication

//@number * 2

802

div

Division

//@number div 2

200.5

mod

Modulus

//@number mod 2

1

 

 


XPath Functions

 

There are also several standard functions available to you to assist in building an XPath query. The standard function library can be broken in to four groups; Node Set, String, Boolean, and Number functions. All XPath implementations should have these functions available. Some XPath implementations may also have an ability to add custom functions. Doing so may cause you to be locked-in to a specific XPath implementation.

 

The following tables offer the syntax, result and a very brief description of each given function. If you need additional information, please follow the URL associated with each group.

 

Node Set Functions

http://www.w3.org/TR/xpath#section-Node-Set-Functions

Function / Syntax

Result

Description

last()

number

Returns the position number of the last node in the current context.

position()

number

Returns the position number of the current node in the current context.

count(nodeset)

number

Returns the number of nodes in the given node set.

id(object)

nodeset

Returns the nodes set for the given unique id.

Requires a document to have DTD.

local-name(nodeset?)

String

Returns the local part of the expanded-name of the given node set. If Argument is not specified it defaults to the context node.

namespace-uri(nodeset?)

string

Returns the namespace part of the expanded-name of the node set. If Argument is not specified it defaults to the context node.

name(nodeset?)

string

Returns the expanded-name of the given node set.

The expanded name consists of the namespace uri followed by a colon then the local name.

If the Argument is not specified it defaults to the context node.

 


 

String Functions

http://www.w3.org/TR/xpath#section-String-Functions

Function / Syntax

Result

Description

string(object?)

string

Converts a given value into a string. If string is not specified it is the string value of the current context node.

concat(string, string, string*)

string

Returns the concatenation of all the passed strings.

starts-with(string, string

boolean

Returns true if the first string starts with the second.

contains(string, string)

boolean

Returns true if the first string contains the second.

substring-before(string, string)

string

Returns the substring of the first string that precedes the first occurrence of the second string.

Returns empty string if Second string not found.

substring-after(string, string)

string

Returns the substring of the first string that follows the first occurrence of the second string.

Returns empty string if Second string not found.

substring(string, number, number?)

string

Returns the substring starting at position of the first number. The number of characters copied is specified by second number, if not specified it copies to the end of the string. (Note: String is 1 based)

string-length(string?)

number

Returns the length of given string. If the string is not specified it returns the length of the current context node.

normalize-space(string?)

string

Removes leading and trailing white spaces, and replaces multiple white spaces contained with-in the string with a single space. If the string is not specified the function is performed on the current context node.

translate(string, string, string)

string

Performs a character by character replacement of values in the first string. Looking for values in the second string, replacing them with the value in the second string at the same position. Can be used to perform ASCII level case conversion.

 


 

Boolean Functions

http://www.w3.org/TR/xpath#section-Boolean-Functions

Function / Syntax

Result

Description

boolean(object)

boolean

Converts a given value into boolean.

not(boolean)

boolean

Reverses the current boolean value; true become false, and false becomes true.

true()

boolean

Returns true.

false()

boolean

Returns false.

lang(string)

boolean

Returns true if the language (specified by xml:lang attribute) of the context node is the same language or a sub language specified by the string argument.

 

Numeric Functions

http://www.w3.org/TR/xpath#section-Number-Functions

Function / Syntax

Result

Description

number(object?)

number

Returns a number representation of the current value passed. If not specified it will use the current context node as its argument.

sum(nodeset)

number

Returns the sum of all the values specified by the nodeset.

floor(number)

number

Returns an Integer value that is the largest possible number that is less than argument.

ceiling(number)

number

Returns an Integer value that is the smallest possible number that is greater than argument.

round(number)

number

Returns an Integer value that is closest to the argument. If two numbers match, the one closest to positive infinity is returned.

 

 


XSLT

 

The Extensible Stylesheet Language (XSL) is comprised of two different parts: XSLT and XSL-FO. XSLT stands for Extensible Stylesheet Language Transformation. XSL-FO stands for Extensible Stylesheet Language Formatting Objects. In this section we will cover XSLT. XSLT can be used independently of XSL-FO. XSL-FO is used to describe how an XML document will be displayed and/or printed. XSL-FO is beyond the scope of this document. XSLT is used to transform one XML document into another textual format, usually a XML document. It should be noted that the resulting XML Document may not be well-formed. It is up to the XSLT author to make sure the result is well formed. XSLT has a broad practical application regardless if a document will ever been seen by a human.

 

XSLT Declaration

 

Since an XSLT document is also a well-formed XML document it must obey all the rules that an XML Document has. The root node of an XSLT document specifies that the document is a transformation document and what version of the specification it is using.

It can use either the stylesheet or transform keyword as both do the exact same thing.

 

The following root node declarations are synonymous with each other so you can use either.

 

<xsl:stylesheet version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform>

</xsl:stylesheet>

 

<xsl:transform version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform>

</xsl:transform>

 

 

Matching and Applying Templates

 

XSLT works by matching a template and then following the rules contained if a match is found. Every XSLT document will contain at least one template. By default the XSLT document is traversed from top to bottom, looking for matches to your templates. Templates do have the option of controlling the matching order and if other templates will be applied to the current match.

 

A template is defined with the following syntax.

 

<xsl:template match=Xpath Query>

Output if XPath Query returns a match.

</xsl:template>

 

 

The following XSLT document will always return the same result, regardless of the source XML document.

 

XSLT

 

 

<!-- Root Element Declaration -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<!-- Template matches on the Root element -->

<xsl:template match="/">

<xmldoc>

This stylesheet will match any document and always produce the same result.

</xmldoc>

</xsl:template>

</xsl:stylesheet>

 

Result

 

<?xml version="1.0"?>

<xmldoc>

This stylesheet will match any document and always produce the same result.

</xmldoc>

 

 


value-of element

 

The previous example was of little practical use, in a real transformation you will want to extract data from the source XML document. To extract a nodes value you can use the value-of function.

 

Syntax:

<xsl:value-of select="/xpath/query"/>

 

Result:

The result is the first node of the nodeset, which was returned by the XPath statement. If the resulting node has child nodes, they will be included in the result. The XPath query can optionally be relative to parent template matches or other previous queries. This will be shown in a later example.

 

The following XSLT document uses a single template and demonstrates the use of

value-of. It is designed to be applied against the training.xml document defined earlier.

 

XSLT

 

<!-- Root Element Declaration -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<!-- Template matches on the Root element -->

<xsl:template match="/">

<xmldoc>

<class><xsl:value-of select="/training/class[1]/name"/></class>

<class><xsl:value-of select="/training/class[2]/name"/></class>

</xmldoc>

</xsl:template>

</xsl:stylesheet>

 

Result

 

<?xml version="1.0"?>

<xmldoc>

<class>Introduction to XML</class>

<class>Introduction to .NET</class>

</xmldoc>

 


for-each element

 

The previous example hard coded the reference to the two class names. However this is broken the second you add more than two class nodes. You either have to change you XSLT document or change your approach. One option you have is to loop through each class element using the for-each element.

 

<xsl:for-each select="/xpath/query">

Output result for each node returned by the xpath query.

</xsl:for-each>

 

The following XSLT document uses a single template and demonstrates the use of

for-each and how XPath is relative to the containing structure

value-of. It is designed to be applied against the training.xml document defined earlier.

 

XSLT

 

<!-- Root Element Declaration -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<!-- Template matches on the Root element -->

<xsl:template match="/">

<xmldoc>

<xsl:for-each select="training/class">

<class>

<xsl:value-of select="name"/>

</class>

</xsl:for-each>

</xmldoc>

</xsl:template>

</xsl:stylesheet>

 

Result

 

<?xml version="1.0"?>

<xmldoc>

<class>Introduction to XML</class>

<class>Introduction to .NET</class>

</xmldoc>


Extending Templates

Naming and Calling Templates

Besides matching a template, we can name and then call it. This allows you to place common functionality in procedural groups.

Template Naming Syntax

<xsl:template name="MyTemplateName">

</xsl:template>

 

To call a template you must use the call-template element.

call-template Syntax

<xsl:call-template name="NameOfTemplateToCall"/>

 

The following XSLT document demonstrates the use of naming and calling templates.

It will produce the same results as the previous example

XSLT

 

<!-- Root Element Declaration -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<!-- Template matches on the Root element -->

<xsl:template match="/">

<xmldoc>

<!-- Apply to all other template matches -->

<xsl:for-each select="/training/class">

<xsl:call-template name="class"/>

</xsl:for-each>

 

</xmldoc>

</xsl:template>

 

<!-- Template match for each /training/class node -->

<xsl:template name="class">

<class><xsl:value-of select="name"/></class>

</xsl:template>

 

</xsl:stylesheet>

Result

 

<?xml version="1.0"?>

<xmldoc>

<class>Introduction to XML</class

<class>Introduction to .NET</class>

</xmldoc>

 

Multiple Match Templates

So far we have shown only one template match in each XSLT document. Building documents that have multiple match templates requires a little more thought. In fact, most complex XSLT documents contain many match templates.

 

The first template that will be matched is the one that contains an XPath statement that is closest to the root node. Additional match templates are triggered through the apply-templates element.

 

apply-templates Syntax

 

<xsl:apply-templates select="/xpath/statement"/>

 

The select attribute allows you to specify an XPATH statement to determine which template matches will be selected. If no template matches are found, then nothing will occur.

 

The following XSLT document uses demonstrates the use of multiple templates and

apply-templates to produce the same results as the previous example.

XSLT

 

<!-- Root Element Declaration -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 

<!-- Template matches on the Root element -->

<xsl:template match="/">

<xmldoc>

<!-- Apply to all other template matches -->

<xsl:apply-templates select="*"/>

</xmldoc>

</xsl:template>

 

<!-- Template match for each /training/class node -->

<xsl:template match="/training/class">

<class><xsl:value-of select="name"/></class>

</xsl:template>

 

</xsl:stylesheet>

Result

 

<?xml version="1.0"?>

<xmldoc>

<class>Introduction to XML</class

<class>Introduction to .NET</class>

</xmldoc>


Recommended References

 

MSDN Xml Development Center

http://msdn.microsoft.com/xml/

 

MSDN Microsoft.Net SDK Documentation

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netstart/html/sdk_netstart.asp

 

Specifically sections on:

System.Xml Namespace

http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemxml.asp

 

System.Xml.Schema Namespace

http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemxmlschema.asp

 

System.Xml.Serialization

http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemxmlserialization.asp

 

System.Xml.XPath

http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemxmlxpath.asp

 

System.Xml.Xsl

http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemxmlxsl.asp

 

XML in a Nutshell (2nd Edition)

Elliotte Rusty Harold & W. Scott Means

ISBN: 0-596-00292-0

 

Essential XML Quick Reference

Aaron Skonnard & Martin Gudgin

ISBN: 0-20174095-8

 

Extensible Markup Language (XML) 1.0 (Third Edition)

http://www.w3.org/TR/REC-xml

 

XML Schema Part 0: Primer

http://www.w3.org/TR/xmlschema-0/

 

XML Schema Part 1: Structures

http://www.w3.org/TR/xmlschema-1/

 

XML Schema Part 2: Datatypes

http://www.w3.org/TR/xmlschema-2/

 

Document Object Model (DOM) Level 1 Specification

http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/

 

XPath Specification

http://www.w3.org/TR/xpath/

 

XSLT Specification

http://www.w3.org/TR/xslt/

 

 

 

Contact Information

 

Robert Love
Peak Biz Solutions

rlove@peakbiz.com
My Blog: http://peakxml.com
Phone: (801) 309-3789

 

 

 

 

 

 

 

 

 

 

  Latest Comments  View All Add New RSS ATOM

Move mouse over comment to see the full text

Server Response from: SC3