Home

Introduction

 

Fundamentals

 

Introduction

 

 

     
 
 

 

   
   
 

Previous Copyright © 2015 FunctionX Next

Home

Visual F# XML: Introduction to XML Elements

 

Elements Fundamentals

 

Introduction

An element in an XML document is an object that begins with a start-tag, may contain a value, and may terminate with an end-tag. Based on this, the combination of a start-tag, the value, and the end-tag is called an element. An element can be more than that but for now, we will consider that an element is primarily characterized by a name and possibly a value.

To support XML elements, the System.Xml namespace provides the XmlElement class. To access an XML element, you can declare a variable of type XmlElement but the main purpose of this class is to get an element from a DOM object. For this reason, the XmlElement class doesn't have a constructor you can use. Instead, and as we will learn, the other classes have methods that produce an XmlElement element you can manipulate as necessary.

We know that every XML file must have a root and you can use the XmlDocument.DocumentElement property to access it. This property is of type XmlElement and, to access it, you can declare an XmlElement variable and assign it this property. Here is an example:

File: videos.xml
<?xml version="1.0" encoding="utf-8"?>
<videos>
    <video>
	<title>The Distinguished Gentleman</title>
	<director>Jonathan Lynn</director>
	<length>112 Minutes</length>
	<format>DVD</format>
	<rating>R</rating>
    </video>
    <video>
	<title>Her Alibi</title>
	<director>Bruce Beresford</director>
	<length>94 Mins</length>
	<format>DVD</format>
	<rating>PG-13</rating>
    </video>
    <video>
	<title>Chalte Chalte</title>
	<director>Aziz Mirza</director>
	<length>145 Mins</length>
	<format>DVD</format>
	<rating>N/R</rating>
    </video>
</videos>

An XML element is represented in the XmlNodeType enumeration as the Element member. When using the Read() method of an XmlTextReader object, to find out if the item being read is an element, you can check whether the member of the current XmlNodeType is Element.

The Name of an Element

The name of an element is the string that represents the tag. For example, in <Director>, the word Director is the name of the element. An element must have at least a start-tag. All of the tags we have seen so far were created as elements. When creating your elements, remember to follow the rules we defined for names.

The XmlElement class is equipped with the Name property that can be used to identify an existing element:

abstract Name : string with get

Here is an example of accessing it:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Exercise",
                        ClientSize = new System.Drawing.Size(130, 52),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Write", Location = new Point(12, 12))

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement

        MessageBox.Show(xeVideo.Name,
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

exercise.Controls.Add btnDocument

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

Notice that videos is returned as the name of the root element of the file. If calling the Read() method of an XmlTextReader object to scan a file, when you get to an element, you can find out its Name identity by accessing it. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Exercise",
                        ClientSize = new System.Drawing.Size(130, 52),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Write", Location = new Point(12, 12))

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        let rdrVideos : XmlTextReader = new XmlTextReader(strFileName)
        xdVideoCollection.Load fsVideo

        while rdrVideos.Read() do
            match rdrVideos.NodeType with
            | XmlNodeType.Element ->
                    MessageBox.Show(rdrVideos.Name,
                    		    "Video Collection",
                    		    MessageBoxButtons.OK,
                    		    MessageBoxIcon.Information) |> ignore
            | _ ->()

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

exercise.Controls.Add btnDocument

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

The Text or Value of an Element

The value of an element is the item displayed on the right side of the start-tag. It is also called the text of the element. In the case of <director>Jonathan Lynn</director>, the "Jonathan Lynn" string is the value of the director element. To support the text or value of an element, the XmlElement class is equipped with the Value property:

abstract Value : string with get, set 
override Value : string with get, set

While the value of one element can be a number, the value of another element can be a date. Yet another element can use a regular string as its value. Consider the following example:

<?xml version="1.0" encoding="utf-8"?>
<videos>
    <video>
	<title>The Distinguished Gentleman</title>
	<director>Jonathan Lynn</director>
	<LengthInMinutes>112</LengthInMinutes>
	<format>DVD</format>
	<rating>R</rating>
	<price>14.95</price>
    </video>
    <video>
	<title>Her Alibi</title>
	<director>Bruce Beresford</director>
	<LengthInMinutes>94</LengthInMinutes>
	<format>VHS</format>
	<rating>PG-13</rating>
	<price>9.95</price>
    </video>
</videos>

Notice that the price elements contain numbers that look like currency values and the LengthInMinutes elements use an integer as value.

If you are using an XmlTextReader object to scan a file, when the Read() method gets to an element, you can find out what its value is by accessing this property. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Exercise",
                        ClientSize = new System.Drawing.Size(130, 52),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Write", Location = new Point(12, 12))

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        let rdrVideos : XmlTextReader = new XmlTextReader(strFileName)
        xdVideoCollection.Load fsVideo

        while rdrVideos.Read() do
            match rdrVideos.NodeType with
            | XmlNodeType.Element ->
                    MessageBox.Show(rdrVideos.Value,
                                    "Video Collection",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information) |> ignore
            | _ ->()

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

exercise.Controls.Add btnDocument

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

The value or text of an element is an object of type XmlText.

Empty Elements

An element may not have a value but only a name. Consider the following example:

<?xml version="1.0" encoding="utf-8"?>
<videos>
  <video>
    <title>The Distinguished Gentleman</title>
    <director>Jonathan Lynn</director>
  </video>
</videos>

In this case, the video element doesn't have a value. It is called an empty element. When a tag is empty, the Value property of its XmlElement object would return an empty value.

Character Entities in an Element Value

Besides the obvious types of values, you may want to display special characters as values of elements. Consider the following example:

<?xml version="1.0" encoding="utf-8" ?>
<Employees>
    <Employee>
	<FullName>Sylvie <Bellie> Aronson</FullName>
	<Salary>25.64</Salary>
	<DepartmentID>1</DepartmentID>
    </Employee>
    <Employee>
	<FullName>Bertrand Yamaguchi</FullName>
	<Salary>16.38</Salary>
	<DepartmentID>4</DepartmentID>
    </Employee>
</Employees>

If you try using this XML document, for example, if you try displaying it in a browser, you would receive an error:

The reason is that when the parser reaches the <FullName>Sylvie <Bellie> Aronson</FullName> line, it thinks that <Bellie> is a tag but then <Bellie> is not closed. The parser concludes that the document is not well-formed, that there is an error. For this reason, to display a special symbol as part of a value, you can use its character code. For example, the < (less than) character is represented with &lt and the > (greater than) symbol can be used with &gt;. Therefore, the above code can be corrected as follows:

<?xml version="1.0" encoding="utf-8" ?>
<Employees>
    <Employee>
	<FullName>Sylvie &lt;Bellie&gt; Aronson</FullName>
	<Salary>25.64</Salary>
	<DepartmentID>1</DepartmentID>
    </Employee>
    <Employee>
	<FullName>Bertrand Yamaguchi</FullName>
	<Salary>16.38</Salary>
	<DepartmentID>4</DepartmentID>
    </Employee>
</Employees>

This would produce:

Here is a list of other codes you can use for special characters:

Code Symbol Code Symbol Code Symbol Code Symbol Code Symbol
&apos; ' &#067; C &#106; j &#179; ³ &#218; Ú
&lt; < &#068; D &#107; k &#180; ´ &#219; Û
&gt; > &#069; E &#108; l &#181; µ &#220; Ü
&amp; & &#070; F &#109; m &#182; &#221; Ý
&quot; " &#071; G &#110; n &#183; · &#222; Þ
&#033; ! &#072; H &#111; o &#184; ¸ &#223; ß
&#034; " &#073; I &#112; p &#185; ¹ &#224; à
&#035; # &#074; J &#113; q &#186; º &#225; á
&#036; $ &#075; K &#114; r &#187; » &#226; â
&#037; % &#076; L &#115; s &#188; ¼ &#227; ã
&#038; & &#077; M &#116; t &#189; ½ &#228; ä
&#039; ' &#078; N &#117; u &#190; ¾ &#229; å
&#040; ( &#079; O &#118; v &#191; ¿ &#230; æ
&#041; ) &#080; P &#119; w &#192; À &#231; ç
&#042; * &#081; Q &#120; x &#193; Á &#232; è
&#043; + &#082; R &#121; y &#194; Â &#233; é
&#044; , &#083; S &#122; z &#195; Ã &#234; ê
&#045; - &#084; T &#123; { &#196; Ä &#235; ë
&#046; . &#085; U &#125; } &#197; Å &#236; ì
&#047; / &#086; V &#126; ~ &#198; Æ &#237; í
&#048; 0 &#087; W &#160; empty &#199; Ç &#238; î
&#049; 1 &#088; X &#161; ¡ &#200; È &#239; ï
&#050; 2 &#089; Y &#162; ¢ &#201; É &#240; ð
&#051; 3 &#090; Z &#163; £ &#202; Ê &#241; ñ
&#052; 4 &#091; [ &#164; ¤ &#203; Ë &#242; ò
&#053; 5 &#092; \ &#165; ¥ &#204; Ì &#243; ó
&#054; 6 &#093; ] &#166; ¦ &#205; Í &#244; ô
&#055; 7 &#094; ^ &#167; § &#206; Î &#245; õ
&#056; 8 &#095; _ &#168; ¨ &#207; Ï &#246; ö
&#057; 9 &#096; ` &#169; © &#208; Ð &#247; ÷
&#058; : &#097; a &#170; ª &#209; Ñ &#248; ø
&#059; ; &#098; b &#171; « &#210; Ò &#249; ù
&#060; < &#099; c &#172; ¬ &#211; Ó &#250; ú
&#061; = &#100; d &#173; ­ &#212; Ô &#251; û
&#062; > &#101; e &#174; ® &#213; Õ &#252; ü
&#063; ? &#102; f &#175; ¯ &#214; Ö &#253; ý
&#064; @ &#103; g &#176; ° &#215; × &#254; þ
&#065; A &#104; h &#177; ± &#216; Ø &#255; ÿ
&#066; B &#105; i &#178; ² &#217; Ù &#256; Ā

There are still other codes to include special characters in an XML file.

Identifying the Markup of a Node

 

The Inner Text of a node

In the previous sections, we have seen how to create a tag to produce a node. We also saw that a node could be placed inside of another node. The combined text of the values of the children of a node is available through its XmlNode.InnerText property which is declared as follows:

abstract InnerText : string with get, set 
override InnerText : string with get, set

This property concatenates the values of the children of the node that called them but doesn't include their markups. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(230, 152),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
let rtbDocument = new RichTextBox(Location = new Point(12, 40), Width = 200)
exercise.Controls.Add btnDocument
exercise.Controls.Add rtbDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement

        rtbDocument.Text <- xeVideo.InnerText

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick


[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

Notice that this property produces all values of the children of a node in one block. We already saw how to access each value of the children of a node by calling the XmlTextReader.Read() method and get its Text.

The Outer XML Code of a Node

If you want to get a node, its markup, its child(ren) and its(their) markup(s), you can access its XmlNode.OuterXml property which is declared as follows:

abstract OuterXml : string with get 
override OuterXml : string with get

Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
let rtbDocument = new RichTextBox(Location = new Point(12, 40), Size = new Size(260, 150))
exercise.Controls.Add btnDocument
exercise.Controls.Add rtbDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement

        rtbDocument.Text <- xeVideo.OuterXml

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick


[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

The Inner XML Code of a Node

If you want only the markup(s) of the child(ren) excluding the parent, access its XmlNode.InnerXml property which is declared as follows:

abstract InnerXml : string with get, set 
override InnerXml : string with get, set

Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
let rtbDocument = new RichTextBox(Location = new Point(12, 40), Size = new Size(260, 150))
exercise.Controls.Add btnDocument
exercise.Controls.Add rtbDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement

        rtbDocument.Text <- xeVideo.InnerXml

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick


[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

The  Child Nodes of  a Node

 

Introduction

As mentioned already, one node can be nested inside of another. A nested node is called a child of the nesting node. This also implies that a node can have as many children as necessary, making them child nodes of the parent node. Once again, consider our videos.xml example:

<?xml version="1.0" encoding="utf-8"?>
<videos>
    <video>
	<title>The Distinguished Gentleman</title>
	<director>Jonathan Lynn</director>
	<length>112 Minutes</length>
	<format>DVD</format>
	<rating>R</rating>
    </video>
    <video>
	<title>Her Alibi</title>
	<director>Bruce Beresford</director>
	<length>94 Mins</length>
	<format>DVD</format>
	<rating>PG-13</rating>
    </video>
    <video>
	<title>Chalte Chalte</title>
	<director>Aziz Mirza</director>
	<length>145 Mins</length>
	<format>DVD</format>
	<rating>N/R</rating>
    </video>
</videos>

The title and the director nodes are children of the video node. The video node is the parent of both the title and the director nodes.

A Collection of XML Nodes

As you may know already, an XML document usually contains many nodes. To consider a collection of nodes, the .NET Framework provides a class named XmlNodeList. It starts as follows:

type XmlNodeList =  
    class 
        interface IEnumerable 
        interface IDisposable 
    end

Linking the Nodes

To manage the relationships among the nodes of an XML document, the .NET Framework provides a class named XmlLinkedNode:

type XmlLinkedNode =  
    class 
        inherit XmlNode 
    end

As a result, many classes derive from XmlLinkedNode. That's the case for the  XmlElement class:

type XmlElement =  
    class 
        inherit XmlLinkedNode 
    end

A Collection of Child Nodes

To support the child nodes of a particular node, the XmlNode class is equipped with a property named ChildNodes. That property is of type XmlNodeList. The property is declared as follows:

abstract ChildNodes : XmlNodeList with get 
override ChildNodes : XmlNodeList with get

When this property is used, it produces an XmlNodeList list, which is a collection of all nodes that share the same parent. Each item in the collection is of type XmlNode. To give you the number of nodes on an XmlNodeList collection, the class is equipped with a property named Count. Here is an example of using it:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement
        let xnlVideos : XmlNodeList = xeVideo.ChildNodes

        MessageBox.Show("The root element contains " + string xnlVideos.Count + " nodes",
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Videos

You can also use the Count property in a for loop to visit the members of the collection.

Accessing a Node in a Collection

The children of a node, that is, the members of a ChildNodes property, or the members of an XmlNodeList collection, can be located each by an index. The first node has an index of 0, the second has an index of 1, an so on. To give you access to a node of the collection, the XmlNodeList class is equipped with an indexed property and a method named Item. Both produce the same result. For example, if a node has three children, to access the third, you can apply an index of 2 to its indexed property. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement
        let xnlVideos : XmlNodeList = xeVideo.ChildNodes

        MessageBox.Show(xnlVideos.[2].InnerText,
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

Videos

You can also use the Item() method to get the same result. Using a for.. in loop, you can access each node and display the values of its children as follows:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement
        let xnlVideos : XmlNodeList = xeVideo.ChildNodes

        for i in 0 .. xnlVideos.Count - 1 do
            MessageBox.Show(xnlVideos.[i].InnerText,
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

Videos

Videos

Videos

Consider the following XML file named PeriodicTable.xml:

<?xml version="1.0" encoding="utf-8"?>
<periodic-table>
	<element>
		<atomic-number>1</atomic-number>
		<element>Hydrogen</element>
		<symbol>H</symbol>
		<atomic-mass>1.0079</atomic-mass>
	</element>
	<element>
		<atomic-number>2</atomic-number>
		<element>Helium</element>
		<symbol>He</symbol>
		<atomic-mass>4.002682</atomic-mass>
	</element>
	<element>
		<atomic-number>3</atomic-number>
		<element>Lithium</element>
		<symbol>Li</symbol>
		<atomic-mass>6.941</atomic-mass>
	</element>
	<element>
		<atomic-number>4</atomic-number>
		<element>Berylium</element>
		<symbol>Be</symbol>
		<atomic-mass>9.0122</atomic-mass>
	</element>
	<element>
		<atomic-number>5</atomic-number>
		<element>Boron</element>
		<symbol>B</symbol>
		<atomic-mass>10.811</atomic-mass>
	</element>
	<element>
		<atomic-number>6</atomic-number>
		<element>Carbon</element>
		<symbol>C</symbol>
		<atomic-mass>12.011</atomic-mass>
	</element>
	<element>
		<atomic-number>7</atomic-number>
		<element>Nitrogen</element>
		<symbol>N</symbol>
		<atomic-mass>14.0067</atomic-mass>
	</element>
	<element>
		<atomic-number>8</atomic-number>
		<element>Oxygen</element>
		<symbol>O</symbol>
		<atomic-mass>15.9994</atomic-mass>
	</element>
	<element>
		<atomic-number>9</atomic-number>
		<element>Fluorine</element>
		<symbol>F</symbol>
		<atomic-mass>18.9984</atomic-mass>
	</element>
	<element>
		<atomic-number>10</atomic-number>
		<element>Neon</element>
		<symbol>Ne</symbol>
		<atomic-mass>20.1797</atomic-mass>
	</element>
</periodic-table>

Every item of a ChildNodes collection is an XmlNode object. This means that every item of a ChildNodes collection possesses its own ChildNodes collection. This also means that, the operations you would perform on a child node can be performed as well as on a child node. This allows you to get the collection of nodes that a child node of another child has. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let chemistry = new Form(ClientSize = new System.Drawing.Size(313, 355),
                            StartPosition = FormStartPosition.CenterScreen,
                            MaximizeBox = false, Text = "Road System Database")

let lvwPeriodicTable  = new ListView(GridLines = true, View = View.Details,
                            Size = new System.Drawing.Size(285, 295),
                            FullRowSelect = true, Location = new Point(12, 14),
                            Anchor = (AnchorStyles.Top ||| AnchorStyles.Bottom ||| AnchorStyles.Left ||| AnchorStyles.Right))

let mutable colElement = lvwPeriodicTable .Columns.Add("Atomic #", 60)
colElement <- lvwPeriodicTable .Columns.Add("Element", 65)
colElement <- lvwPeriodicTable .Columns.Add("Symbol", 60, HorizontalAlignment.Center)
colElement <- lvwPeriodicTable .Columns.Add("Atomic Mass", 80, HorizontalAlignment.Right)
chemistry.Controls.Add lvwPeriodicTable 

let chemistryLoad e =
    let xdPeriodicTable : XmlDocument = new XmlDocument()
    let strElementsFile = "../../PeriodicTable.xml"

    if File.Exists strElementsFile then
        lvwPeriodicTable.Items.Clear()

        use fsPeriodicTable : FileStream = new FileStream(strElementsFile, FileMode.Open, FileAccess.Read)
        xdPeriodicTable.Load fsPeriodicTable

        let xeElement : XmlElement = xdPeriodicTable.DocumentElement
        let xnlElements : XmlNodeList = xeElement.ChildNodes
        
        for i in 0 .. xnlElements.Count - 1 do
            let xnElement : XmlNode = xnlElements.[i]

            let xnlProperties : XmlNodeList = xnElement.ChildNodes

            let lviElement : ListViewItem = new ListViewItem(xnlProperties.[0].InnerText)
            
            let mutable lviTemp = lviElement.SubItems.Add xnlProperties.[1].InnerText
            lviTemp <- lviElement.SubItems.Add xnlProperties.[2].InnerText
            lviTemp <- lviElement.SubItems.Add xnlProperties.[3].InnerText

            lvwPeriodicTable .Items.Add lviElement |> ignore
chemistry.Load.Add chemistryLoad

let btnClose = new Button(Text = "Close", 
                          Location = new System.Drawing.Point(222, 322),
                          Anchor = (AnchorStyles.Bottom ||| AnchorStyles.Right))
chemistry.Controls.Add btnClose

let btnCloseClick e = chemistry.Close()
btnClose.Click.Add btnCloseClick

[<EntryPoint>]
let main argv = 
    Application.Run chemistry
    0

This would produce:

Accessing a Node in a Collection

Instead of using the indexed property, the XmlNodeList class implements the IEnumerable interface. This allows you to use a for .. in loop to visit each node of the collection. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(285, 205),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement
        let xnlVideos : XmlNodeList = xeVideo.ChildNodes

        for node in xnlVideos do
            MessageBox.Show(node.InnerText,
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                            "Video Collection",
                            MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

To better manage and manipulate the nodes of a collection of nodes, you must be able to access the desired node. The XmlNode class combined with the XmlNodeList class provide various means of getting to a node and taking the appropriate actions.

The Parent of a Node

Not all nodes have children, obviously. For example, the title node of our videos.xml file does not have children. To find out whether a node has children, check its HasChildNodes Boolean property that is declared as follows:

abstract HasChildNodes : bool with get 
override HasChildNodes : bool with get

If a node is a child, to get its parent, you can access its ParentNode property:

abstract ParentNode : XmlNode with get 
override ParentNode : XmlNode with get

The First Child Node

The children of a nesting node are also recognized by their sequence. For our videos.xml file, the first line is called the first child of the DOM. This would be:

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

After identifying or locating a node, the first node that immediately follows it is referred to as its first child. In our videos.xml file, the first child of the first video node is the <title>The Distinguished Gentleman</title> element. The first child of the second <video> node is <title>Her Alibi</title>.

In the .NET Framework, the first child of a node can be retrieved by accessing the XmlNode.FirstChild property declared as follows:

abstract FirstChild : XmlNode with get 
override FirstChild : XmlNode with get

In the following example, every time the parser gets to a video node, it displays the value of it first child:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 110),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let lbxVideos = new ListBox(Location = new Point(12, 40), Size = new Size(200, 60));
exercise.Controls.Add lbxVideos

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xeVideo : XmlElement = xdVideoCollection.DocumentElement
        let xnlVideos : XmlNodeList = xeVideo.ChildNodes

        for node in xnlVideos do
            lbxVideos.Items.Add(node.FirstChild.InnerText) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

In this example, we started our parsing on the root node of the document. At times, you will need to consider only a particular node, such as the first child of a node. For example, you may want to use only the first child of the root. To get it, you can access the FirstChild property of the DocumentElement object of the DOM. Once you get that node, you can then do what you judge necessary. In the following example, only the values of the child nodes of the first child of the root are displayed:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 120),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let lbxVideos = new ListBox(Location = new Point(12, 40), Size = new Size(200, 70));
exercise.Controls.Add lbxVideos

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()
    
    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo

        let xnVideo : XmlNode = xdVideoCollection.DocumentElement.FirstChild
        let xnlVideos : XmlNodeList = xnVideo.ChildNodes

        for child in xnlVideos do
            lbxVideos.Items.Add(child.InnerText) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

Consider the following modification of the Videos.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<videos>
    <video>
	<title>The Distinguished Gentleman</title>
	<director>Jonathan Lynn</director>
	<CastMembers>
	    <actor>Eddie Murphy</actor>
	    <actor>Lane Smith</actor>
	    <actor>Sheryl Lee Ralph</actor>
	    <actor>Joe Don Baker</actor>
	    <actor>Victoria Rowell</actor>
	</CastMembers>
	<length>112 Minutes</length>
	<format>DVD</format>
	<rating>R</rating>
    </video>
    <video>
	<title>Her Alibi</title>
	<director>Bruce Beresford</director>
	<length>94 Mins</length>
	<format>DVD</format>
	<rating>PG-13</rating>
    </video>
    <video>
	<title>Chalte Chalte</title>
	<director>Aziz Mirza</director>
	<length>145 Mins</length>
	<format>DVD</format>
	<rating>N/R</rating>
    </video>
</videos>

Remember that when using a for .. in loop applied to an XmlNodeList collection, each node that you access is a complete XmlNode object and may have children. This means that you can further get the ChildNodes node of any node. Here is an example that primarily scans the nodes but looks for one whose name is CastMembers:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 120),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let lbxVideos = new ListBox(Location = new Point(12, 40), Size = new Size(200, 70));
exercise.Controls.Add lbxVideos

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()

    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo
        
        // Locate the root node and 
        // get a reference to its first child
        let xnVideo : XmlNode = xdVideoCollection.DocumentElement.FirstChild
        // Create a list of the child nodes of 
        // the first node under the root
        let xnlVideos : XmlNodeList = xnVideo.ChildNodes

        for i in 0 .. xnlVideos.Count - 1 do
            // Look for a node named CastMembers
            if xnlVideos.[i].Name = "cast-members" then
                // Once/if you find it,
                // 1. Access its first child
                // 2. Create a list of its child nodes
                let xnlActors : XmlNodeList = xnlVideos.[i].ChildNodes
                // Display the values of the nodes
                for j in 0 .. xnlActors.Count - 1 do
                    lbxVideos.Items.Add(xnlActors.[j].InnerText) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

As we have learned that a node or a group of nodes can be nested inside of another node. When you get to a node, you may know or find out that it has children. You may then want to consider only the first child. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 120),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Document", Location = new Point(12, 12))
exercise.Controls.Add btnDocument

let lbxVideos = new ListBox(Location = new Point(12, 40), Size = new Size(200, 70));
exercise.Controls.Add lbxVideos

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"
    let xdVideoCollection : XmlDocument = new XmlDocument()

    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)
        xdVideoCollection.Load fsVideo
        
        // Locate the root node and 
        // get a reference to its first child
        let xnVideo : XmlNode = xdVideoCollection.DocumentElement.FirstChild
        // Create a list of the child nodes of 
        // the first node under the root
        let xnlVideos : XmlNodeList = xnVideo.ChildNodes

        for i in 0 .. xnlVideos.Count - 1 do
            // Look for a node named CastMembers
            if xnlVideos.[i].Name = "cast-members" then
                // Once/if you find it,
                // 1. Access its first child
                // 2. Create a list of its child nodes
                let xnlActors : XmlNodeList = xnlVideos.[i].FirstChild.ChildNodes
                // Display the values of the nodes
                for j in 0 .. xnlActors.Count - 1 do
                    lbxVideos.Items.Add(xnlActors.[j].InnerText) |> ignore

        fsVideo.Close()
    else
        MessageBox.Show("The file " + strFileName + " was not found",
                        "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Video Collection

The Last Child Node

As opposed to the first child, the child node that immediately precedes the end-tag of the parent node is called the last child. To get the last child of a node, you can access its XmlNode.LastChild property that is declared as follows:

abstract LastChild : XmlNode with get 
override LastChild : XmlNode with get

The Siblings of a Node

The child nodes that are nested in a parent node and share the same level are referred to as siblings. Consider the above file: Director, CastMembers, and Length are child nodes of the Video node but the Actor node is not a child of the Video node. Consequently, Director, CastMembers, and Length are siblings. Obviously, to get a sibling, you must first have a node.

To access the sibling of a node, you can use its XmlNode.NextSibling property, which is declared as follows:

abstract NextSibling : XmlNode with get 
override NextSibling : XmlNode with get

Once you have a node, such as getting it from the first or the last child, you can directly get its next sibling as we saw previously. Here is an example:

let roadDatabaseLoad e =
    let xdRoads : XmlDocument = new XmlDocument()
    let strRoadsFile = "../../Roads.xml";

    if File.Exists strRoadsFile then
        lvwRoads.Items.Clear()

        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads
        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xeRoad.ChildNodes
        
        for xnRoad : XmlNode in xnlRoads do
            let lviRoad = new ListViewItem(xnRoad.FirstChild.InnerText) // Road Name
                
            lvwRoads.Items.Add lviRoad |> ignore

roadDatabase.Load.Add roadDatabaseLoad

We saw earlier that the first and the last nodes are of type XmlNode. This means that, once you have one of those nodes, you can access its next sibling. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let roadDatabase = new Form(ClientSize = new System.Drawing.Size(212, 195),
                            StartPosition = FormStartPosition.CenterScreen,
                            MaximizeBox = false, Text = "Road System Database")

let lvwRoads = new ListView(GridLines = true, View = View.Details,
                            Size = new System.Drawing.Size(186, 124),
                            FullRowSelect = true, Location = new Point(12, 15))

let mutable colRoad = lvwRoads.Columns.Add("Road Name", 70)
colRoad <- lvwRoads.Columns.Add("Road Type", 108)
roadDatabase.Controls.Add lvwRoads

let roadDatabaseLoad e =
    let xdRoads : XmlDocument = new XmlDocument()
    let strRoadsFile = "../../Roads.xml";

    if File.Exists strRoadsFile then
        lvwRoads.Items.Clear()

        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads
        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xeRoad.ChildNodes
        
        for xnRoad : XmlNode in xnlRoads do
            let lviRoad = new ListViewItem(xnRoad.FirstChild.InnerText) // Road Name
                
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.InnerText |> ignore // Road Type
            lvwRoads.Items.Add lviRoad |> ignore

roadDatabase.Load.Add roadDatabaseLoad

// Button: Close
let btnClose = new Button(Location = new System.Drawing.Point(124, 156),
                          Text = "Close", Size = new System.Drawing.Size(75, 23))
roadDatabase.Controls.Add btnClose

let btnCloseClick e = roadDatabase.Close()
btnClose.Click.Add btnCloseClick

[<EntryPoint>]
let main argv = 
    Application.Run roadDatabase
    0

This would produce:

The Siblings of a Node

In fact, from the next sibling of a first or last node, you can keep calling the next sibling until you reach the desired sibling, that is, until you reach the actual node you want to access. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let roadDatabase = new Form(ClientSize = new System.Drawing.Size(728, 380),
                            StartPosition = FormStartPosition.CenterScreen,
                            MaximizeBox = false, Text = "Road System Database")

let lvwRoads = new ListView(GridLines = true, View = View.Details,
                            Size = new System.Drawing.Size(702, 295),
                            FullRowSelect = true, Location = new Point(12, 15),
                            Anchor = (AnchorStyles.Top ||| AnchorStyles.Bottom ||| AnchorStyles.Left ||| AnchorStyles.Right))

let mutable colRoad = lvwRoads.Columns.Add("Road Name", 70)
colRoad <- lvwRoads.Columns.Add("Road Type", 85)
colRoad <- lvwRoads.Columns.Add("Distance(Miles)", 85)
colRoad <- lvwRoads.Columns.Add("Location", 440)
roadDatabase.Controls.Add lvwRoads

let roadDatabaseLoad e =
    let xdRoads : XmlDocument = new XmlDocument()
    let strRoadsFile = "../../Roads.xml";

    if File.Exists strRoadsFile then
        lvwRoads.Items.Clear()

        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads
        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xeRoad.ChildNodes
        
        for xnRoad : XmlNode in xnlRoads do
            let lviRoad = new ListViewItem(xnRoad.FirstChild.InnerText) // Road Name
                
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.InnerText |> ignore // Road Type
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.NextSibling.InnerText |> ignore // Distance
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.NextSibling.NextSibling.InnerText |> ignore // Location

            lvwRoads.Items.Add lviRoad |> ignore

roadDatabase.Load.Add roadDatabaseLoad

// Button: Close
let btnClose = new Button(Location = new System.Drawing.Point(570, 330),
                          Text = "Close", Size = new System.Drawing.Size(145, 36),
                          Anchor = (AnchorStyles.Bottom ||| AnchorStyles.Right))
roadDatabase.Controls.Add btnClose

let btnCloseClick e = roadDatabase.Close()
btnClose.Click.Add btnCloseClick

[<EntryPoint>]
let main argv = 
    Application.Run roadDatabase
    0

This would produce:

The Siblings of a Node

Locating an Element

 

Introduction

In some cases, you may want to perform an operation on an existing and particular node. For example, you may want to change the value of a node, you may want to add a new child node to an existing node, etc. Before taking any of these actions, you must be able to locate or identify the desired element.

Locating an element consists of looking for a particular node among the nodes. To do this, you must start somewhere. Obviously, the first node you can identify is the root. Once you get the root, you can then get a collection of its children. After getting a collection of the children of the root, you can locate a node in the collection. If the node you are looking for is a child of that first collection, you can then get a collection of the child nodes of that node and proceed.

Fortunately, the System.Xml namespace provides various means of looking for a node in an XML file.

Locating an Element Using its Index

Consider the following XML file named videos.xml:

<?xml version="1.0" encoding="utf-8"?>
<videos>
    <video>
	<title>The Distinguished Gentleman</title>
	<director>Jonathan Lynn</director>
	<length>112 Minutes</length>
	<format>DVD</format>
	<rating>R</rating>
    </video>
    <video>
	<title>Her Alibi</title>
	<director>Bruce Beresford</director>
	<length>94 Minutes</length>
	<format>DVD</format>
	<rating>PG-13</rating>
    </video>
    <video>
	<title>The Day After Tomorrow</title>
	<director>Roland Emmerich</director>
	<length>124 Minutes</length>
	<format>DVD</format>
	<rating>PG-13</rating>
    </video>
    <video>
	<title>Other People&#039;s Money</title>
	<director>Alan Brunstein</director>
	<length>114 Minutes</length>
	<format>VHS</format>
	<rating>PG-13</rating>
    </video>
</videos>

We know that the XmlNodeList class is equipped with an indexed propery. Its signatures is:

abstract ItemOf :
        i : int -> XmlNode with get
override ItemOf :
        i : int -> XmlNode with get

For the same goal, the class is equipped with a method named Item:

abstract Item :
        index : int -> XmlNode

These two members allow you to access an element based on its index. Here are examples:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 120),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Start XML File", Location = new Point(12, 12), Width = 100)
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"

    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)

        let xdVideoCollection : XmlDocument = new XmlDocument()
        xdVideoCollection.Load fsVideo
        let xeRoot : XmlElement = xdVideoCollection.DocumentElement
        
        let xnlVideos : XmlNodeList = xeRoot.ChildNodes

        MessageBox.Show(xnlVideos.[1].InnerText, "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore
        MessageBox.Show(xnlVideos.Item(3).InnerXml, "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

This would produce:

Locating an Element Using its Index

Locating an Element Using its Index

You can use this characteristic to locate a node. Because XML is very flexible with the names (you can have two child nodes that have the same name) and values (you can have two child nodes that have the same value) of nodes, when creating an XML file, it is your responsibility to create a scheme that would eventually allow you to uniquely identify each element.

Locating an Element Using its Name

To assist you with locating the first child node of a node, the XmlNode class is equipped with an indexed property (named Item) overloaded with two versions. One of the versions is declared as follows:

abstract Item :
        name : string -> XmlElement with get
override Item :
        name : string -> XmlElement with get

This indexed property takes the name of a node as argument. After the property has been called, the parser checks the child nodes of the element on which the property was applied. If the parser finds a node with that name, it returns it as an XmlElement object. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let exercise = new Form(MaximizeBox = false, Text = "Video Collection",
                        ClientSize = new System.Drawing.Size(225, 120),
                        StartPosition = FormStartPosition.CenterScreen)

let btnDocument = new Button(Text = "Start XML File", Location = new Point(12, 12), Width = 100)
exercise.Controls.Add btnDocument

let btnDocumentClick _ =
    let strFileName = "..\..\Videos.xml"

    if File.Exists(strFileName) then
        use fsVideo = new FileStream(strFileName, FileMode.Open, FileAccess.Read)

        let xdVideoCollection : XmlDocument = new XmlDocument()
        xdVideoCollection.Load fsVideo
        let xeRoot : XmlElement = xdVideoCollection.DocumentElement
        
        let xnlVideos : XmlNodeList = xeRoot.ChildNodes

        MessageBox.Show(xnlVideos.[1].["director"].InnerText, "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore
        MessageBox.Show(xnlVideos.Item(3).["format"].InnerXml, "Video Collection",
                        MessageBoxButtons.OK, MessageBoxIcon.Information) |> ignore

        fsVideo.Close()

btnDocument.Click.Add btnDocumentClick

[<EntryPoint>]
let main argv = 
    Application.Run exercise
    0

Based on the videos.xml file we had earlier, this would produce:

Locating an Element Using its Name Locating an Element Using its Name

If the node has more than one child with the same name, then it would return the first child with that name. You can use this characteristic to look for, or locate, an element.

Locating an Element Using a Tag Name

To assist you with finding a node, the XmlDocument class is equipped with the GetElementByTagName() method which is overloaded with two versions. One of the signatures used is:

abstract GetElementsByTagName : 
        name:string -> XmlNodeList  
override GetElementsByTagName : 
        name:string -> XmlNodeList

This method takes as argument a string. The string must be the name of a node. If at least one node that holds that name exists in the file, this method returns a collection of the nodes with that name. If there is no node with that name, the collection is returned empty and there is no exception thrown.

Here is an example of calling the method:

let lvwRoadsItemSelectionChanged (e : ListViewItemSelectionChangedEventArgs) =
    let xdRoads : XmlDocument = new XmlDocument()

    if File.Exists(strRoadsFile) then
        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads

        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xdRoads.GetElementsByTagName("road-name")

lvwRoads.ItemSelectionChanged.Add lvwRoadsItemSelectionChanged

Once you have a list of the nodes of a particular criterion, you can then act as you see fit. For example, you can look for a particular node that holds a text of your choice. Here is an example:

open System
open System.IO
open System.Xml
open System.Drawing
open System.Windows.Forms

let roadDatabase = new Form(ClientSize = new System.Drawing.Size(728, 545),
                            StartPosition = FormStartPosition.CenterScreen,
                            MaximizeBox = false, Text = "Road System Database")

let lvwRoads = new ListView(GridLines = true, View = View.Details,
                            Size = new System.Drawing.Size(702, 265),
                            FullRowSelect = true, Location = new Point(12, 15),
                            Anchor = (AnchorStyles.Top ||| AnchorStyles.Bottom ||| AnchorStyles.Left ||| AnchorStyles.Right))

let mutable colRoad = lvwRoads.Columns.Add("Road Name", 70)
colRoad <- lvwRoads.Columns.Add("Road Type", 85)
colRoad <- lvwRoads.Columns.Add("Distance(Miles)", 85)
colRoad <- lvwRoads.Columns.Add("Location", 440)
roadDatabase.Controls.Add lvwRoads

let lvwIntersections = new ListView(GridLines = true, View = View.Details,
                            Size = new System.Drawing.Size(376, 225),
                            FullRowSelect = true, Location = new Point(160, 300),
                            Anchor = (AnchorStyles.Bottom ||| AnchorStyles.Left ||| AnchorStyles.Right))

let mutable colIntersection = lvwIntersections.Columns.Add("Road", 50)
colIntersection <- lvwIntersections.Columns.Add("Intersects With", 120)
colIntersection <- lvwIntersections.Columns.Add("In/Near", 180)
roadDatabase.Controls.Add lvwIntersections

let roadDatabaseLoad e =
    let xdRoads : XmlDocument = new XmlDocument()
    let strRoadsFile = "../../Roads.xml";

    if File.Exists strRoadsFile then
        lvwRoads.Items.Clear()

        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads
        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xeRoad.ChildNodes
        
        for xnRoad : XmlNode in xnlRoads do
            let lviRoad = new ListViewItem(xnRoad.FirstChild.InnerText) // Road Name
                
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.InnerText |> ignore // Road Type
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.NextSibling.InnerText |> ignore // Distance
            lviRoad.SubItems.Add xnRoad.FirstChild.NextSibling.NextSibling.NextSibling.InnerText |> ignore // Location

            lvwRoads.Items.Add lviRoad |> ignore
roadDatabase.Load.Add roadDatabaseLoad

let lvwRoadsItemSelectionChanged (e : ListViewItemSelectionChangedEventArgs) =
    let xdRoads : XmlDocument = new XmlDocument()
    let roadSelected : string = e.Item.SubItems.[0].Text
    let strRoadsFile : string = "../../Roads.xml"

    lvwIntersections.Items.Clear()

    if File.Exists(strRoadsFile) then
        use fsRoads : FileStream = new FileStream(strRoadsFile, FileMode.Open, FileAccess.Read)
        xdRoads.Load fsRoads

        let xeRoad : XmlElement = xdRoads.DocumentElement
        let xnlRoads : XmlNodeList = xdRoads.GetElementsByTagName("road-name")

        for xnRoad in xnlRoads do
            if xnRoad.InnerText = roadSelected then
                let xnlIntersections : XmlNodeList = xnRoad.NextSibling.NextSibling.NextSibling.NextSibling.ChildNodes

                for i in 0 .. xnlIntersections.Count - 1 do
                    let lviIntersection = new ListViewItem(roadSelected) // Selected Road
                    lviIntersection.SubItems.Add xnlIntersections.[i].ChildNodes.[0].InnerText |> ignore // Intersects With
                    lviIntersection.SubItems.Add xnlIntersections.[i].ChildNodes.[1].InnerText |> ignore // In or Near

                    lvwIntersections.Items.Add lviIntersection |> ignore

        fsRoads.Close()

lvwRoads.ItemSelectionChanged.Add lvwRoadsItemSelectionChanged

// Button: Close
let btnClose = new Button(Location = new System.Drawing.Point(554, 493),
                          Text = "Close", Size = new System.Drawing.Size(145, 36),
                          Anchor = (AnchorStyles.Bottom ||| AnchorStyles.Right))
roadDatabase.Controls.Add btnClose

let btnCloseClick e = roadDatabase.Close()
btnClose.Click.Add btnCloseClick

[<EntryPoint>]
let main argv = 
    Application.Run roadDatabase
    0

This would produce:

Locating an Element Using a Tag Name

Locating an Element Using a Tag Name

Locating an Element Using a Tag Name

Locating an Element Using a Tag Name


Home Copyright © 2015 FunctionX Home