Tuesday, July 1, 2008

.NET Xml

An Introduction to Working with XML in .NET

Working with XML in .NET applications is a fairly straightforward process especially if you've used MSXML3 in the past. Even if you haven't worked with MSXML3 you'll find that using the classes found in the .NET platform is easy once you know more about them.

There are two main APIs that you can use to access data found within XML documents. These include forward-only, non-cached access, and random access through using the Document Object Model (DOM). The classes that expose both APIs are found within the System.Xml assembly.

If you want to access data within an XML document in the most fast and efficient way possible then you'll want to use the XmlTextReader class. This class exposes a pull model that has many advantages over the push model found in the Simple API for XML (SAX). To use this class you must reference the System.Xml assembly in your file. In C# you would do this with the "using" keyword while in Visual Basic you would use the "imports" keyword. Once the assembly is referenced, you can then instantiate the reader as shown below:

    XmlTextReader reader = new XmlTextReader(pathToXmlDoc);
    int elementCount = 0;
    while (reader.Read()) {
        if (reader.NodeType == XmlNodeType.Element) {
            elementCount++;
        }
    }

There are several different constructors available for the XmlTextReader class. The one shown above accepts an XML document file path as a string argument.

While the forward-only pull model is certainly very efficient, it is read-only and therefore doesn't allow you to insert, delete, or update nodes in a document. In cases where you need more control or flexibility over an XML document, you'll want to look at using the Document Object Model (DOM). The DOM API works by loading each node found within an XML document into a tree structure similar to a genealogical chart. By having this in-memory structure, random access to different nodes within an XML document is possible.

To start the process of creating the DOM tree, you need to reference the System.Xml assembly in your file and then instantiate the XmlDocument class:

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.Load(pathToXmlDoc);
    XmlNode root = xmlDoc.DocumentElement;

Adding nodes to the tree can easily be accomplished by using methods associated with the XmlDocument class. The following example shows how to load XML from a file and then add an element as a child of a root node named "root." It also shows how an attribute can be added to a node as well:

    XmlDocument xmlDoc = new XmlDocument();
    XmlDoc.Load(pathToXmlDoc);
    XmlElement root = xmlDoc.DocumentElement;
    XmlElement newNode = doc.CreateElement("newNode");
    newNode.SetAttribute("id","1");
    root.AppendChild(newNode);

This code would result in the following XML document:



In cases where a string containing XML needs to be loaded into the DOM, the XmlDocument class's LoadXml() method can be used. Once in the DOM, the XML can then be manipulated as shown above:

    string myXml = "Hello";
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(myXml);
    //....manipulation or reading of the nodes can occur here

There are many other classes that can perform a variety of tasks in the System.Xml assembly. This article has barely scratched the surface but has hopefully shown that much of your experience with MSXML3 can be used in .NET applications as well. Using many of the other XML related classes in the .NET platform will be detailed in future articles so stay tuned!

Reading XML Files using XmlDocument in VB.NET

Suppose I have following XML fragment:



 Now, how can I loop through my collection of authors and for each author  retrieve its first and last name and put them in a variable strFirst and  strLast?

 - - - XMLApp.vb

Imports System
Imports System.Xml

_
Public Class XMLApp

Public Sub YourMethod(strFirst As [String], strLast As [String])
' Do something with strFirst and strLast.
' ...
Console.WriteLine("{0}, {1}", strLast, strFirst)
End Sub 'YourMethod


Public Sub ProcessXML(xmlText As [String])
Dim _doc As New XmlDocument()
_doc.LoadXml(xmlText)
' alternately, _doc.Load( _strFilename); to read from a file.
Dim _fnames As XmlNodeList = _doc.GetElementsByTagName("FirstName")
Dim _lnames As XmlNodeList = _doc.GetElementsByTagName("LastName")

' I'm assuming every FirstName has a LastName in this example, your requirements may vary. // for ( int _i = 0; _i < _fnames.Count; ++_i )
If (True) Then
YourMethod(_fnames(_i).InnerText, _lnames(_i).InnerText)
End If

Dim Main As

[String]()
If (True) Then
Dim _app As New XMLApp()
' Passing XML text as a String, you can also use the
' XMLDocument::Load( ) method to read the XML from a file.
'




End If '

End Sub 'ProcessXML
End Class 'XMLApp

' end XMLApp

  - - - XMLApp.vb


 Remember to /reference the System.Xml.dll on the command-line  to build XMLApp.vb:
 vbc.exe /r:System.Xml.dll XMLApp.vb


XML Programming using VB.NET

XML has been an integral part of programming these days since the world of programming is moving towards the web. XML is one of the most useful easier ways to store and transfer data. Microsoft .NET framework utilizes XML to store data and transfer data between applications and remote systems including local, intranet or on the Internet.

In this article we will explore XML classes and namespaces of .NET Framework Class Library and how to use them in VB.NET to read, write and navigate XML documents.

XML Namespaces and Classes
Microsoft .NET Runtime Library contains many classes to work with XML documents. These classes are stored in five namespaces - System.Xml, System.Xml.Schema, System.Xml.Serialization, System.Xml.XPath, and System.Xml.Xsl. The purpose of each of these namespace is different. All classes reside in one single assembly System.Xml.dll assembly.

Before you proceed to use XML classes in your programs, you need to add reference to the System.Xml.dll assembly and use namespaces in your program by using Imports keyword.

First namespace is System.Xml. This namespace contains major classes. This namespace contains many classes to read and write XML documents. In this article, we are going to concentrate on reader and write class. These reader and writer classes are used to read and write XMl documents. These classes are - XmlReader, XmlTextReader, XmlValidatingReader, XmlNodeReader, XmlWriter, and XmlTextWriter. As you can see there are four reader and two writer classes.

The XmlReader class is an abstract bases classes and contains methods and properties to read a document. The Read method reads a node in the stream. Besides reading functionality, this class also contains methods to navigate through a document nodes. Some of these methods are MoveToAttribute, MoveToFirstAttribute, MoveToContent, MoveToFirstContent, MoveToElement and MoveToNextAttribute. ReadString, ReadInnerXml, ReadOuterXml, and ReadStartElement are more read methods. This class also has a method Skip to skip current node and move to next one. We’ll see these methods in our sample example.

The XmlTextReader, XmlNodeReader and XmlValidatingReader classes are derived from XmlReader class. As their name explains, they are used to read text, node, and schemas.

The XmlWrite class contains functionality to write data to XML documents. This class provides many write method to write XML document items. This class is base class for XmlTextWriter class, which we’ll be using in our sample example.

The XmlNode class plays an important role. Although, this class represents a single node of XML but that could be the root node of an XML document and could represent the entire file. This class is an abstract base class for many useful classes for inserting, removing, and replacing nodes, navigating through the document. It also contains properties to get a parent or child, name, last child, node type and more. Three major classes derived from XmlNode are XmlDocument, XmlDataDocument and XmlDocumentFragment. XmlDocument class represents an XML document and provides methods and properties to load and save a document. It also provides functionality to add XML items such as attributes, comments, spaces, elements, and new nodes. The Load and LoadXml methods can be used to load XML documents and Save method to save a document respectively. XmlDocumentFragment class represents a document fragment, which can be used to add to a document. The XmlDataDocument class provides methods and properties to work with ADO.NET data set objects.

In spite of above discussed classes, System.Xml namespace contains more classes. Few of them are XmlConvert, XmlLinkedNode, and XmlNodeList.

Next namespace in Xml series is System.Xml.Schema. It classes to work with XML schemas such XmlSchema, XmlSchemaAll, XmlSchemaXPath, XmlSchemaType.

The System.Xml.Serialization namespace contains classes that are used to serialize objects into XML format documents or streams.

The System.Xml.XPath Namespce contains XPath related classes to use XPath specifications. This namespace has following classes – XPathDocument, XPathExression, XPathNavigator, and XPathNodeIterator.

With the help of XpathDocument, XpathNavigator provides a fast navigation though XML documents. This class contains many Move methods to move through a document.
The System.Xml.Xsl namespace contains classes to work with XSL/T transformations.

Reading XML Documents
In my sample application, I’m using books.xml to read and display its data through XmlTextReader. This file comes with VS.NET samples. You can search this on your machine and change the path of the file in the following line:

Dim textReader As XmlTextReader = New XmlTextReader("books.xml") 

Or you can use any XML file.

The XmlTextReader, XmlNodeReader and XmlValidatingReader classes are derived from XmlReader class. Besides XmlReader methods and properties, these classes also contain members to read text, node, and schemas respectively.

I am using XmlTextReader class to read an XML file. You read a file by passing file name as a parameter in constructor.

Dim textReader As XmlTextReader = New XmlTextReader("books.xml")

After creating an instance of XmlTextReader, you call Read method to start reading the document. After read method is called, you can read all information and data stored in a document. XmlReader class has properties such as Name, BaseURI, Depth, LineNumber an so on.

List 1 reads a document and displays a node information using these properties.

List 1: Reading an XML document Properties

' Import System.Xml namespace
Imports System.Xml

Module Module1

Sub Main()

' Create an isntance of XmlTextReader and call Read method to read
the file
Dim textReader As XmlTextReader = New
XmlTextReader("C:\\books.xml")
textReader.Read()

' If the node has value
If textReader.HasValue Then
' Move to fist element
textReader.MoveToElement()
Console.WriteLine("XmlTextReader Properties Test")
Console.WriteLine("===================")

' Read this element's properties and display them on console
Console.WriteLine("Name:" + textReader.Name)
Console.WriteLine("Base URI:" + textReader.BaseURI)
Console.WriteLine("Local Name:" + textReader.LocalName)
Console.WriteLine("Attribute Count:" +textReader.AttributeCount.ToString())
Console.WriteLine("Depth:" + textReader.Depth.ToString())
Console.WriteLine("Line Number:" +
textReader.LineNumber.ToString())
Console.WriteLine("Node Type:" +
textReader.NodeType.ToString())
Console.WriteLine("Attribute Count:" +
textReader.Value.ToString())
End If
End Sub
End Module
 
The NodeType property of XmlTextReader is important when you want to know the content type of a document. The XmlNodeType enumeration has a member for each type of XML item such as Attribute, CDATA, Element, Comment, Document, DocumentType, Entity, ProcessInstruction, WhiteSpace and so on.

List 2 code sample reads an XML document, finds a node type and writes information at the end with how many node types a document has. 

List 2. Retrieving NodeType Information 

' Import System.Xml namespace
Imports System.Xml

Module Module1

Sub Main()

Dim ws, pi, dc, cc, ac, et, el, xd As Integer

' Read a document
Dim textReader As XmlTextReader = New
XmlTextReader("C:\\books.xml")

' Read until end of file
While textReader.Read()

Dim nType As XmlNodeType = textReader.NodeType

' If node type us a declaration
If nType = XmlNodeType.XmlDeclaration Then
Console.WriteLine("Declaration:" +
textReader.Name.ToString())
xd = xd + 1
End If
' if node type is a comment
If nType = XmlNodeType.Comment Then
Console.WriteLine("Comment:" + textReader.Name.ToString())
cc = cc + 1
End If
' if node type us an attribute
If nType = XmlNodeType.Attribute Then
Console.WriteLine("Attribute:" +
textReader.Name.ToString())
ac = ac + 1
End If
' if node type is an element
If nType = XmlNodeType.Element Then
Console.WriteLine("Element:" + textReader.Name.ToString())
el = el + 1
End If
' if node type is an entity
If nType = XmlNodeType.Entity Then
Console.WriteLine("Entity:" + textReader.Name.ToString())
et = et + 1
End If

' if node type is a Process Instruction
If nType = XmlNodeType.Entity Then
Console.WriteLine("Entity:" + textReader.Name.ToString())
pi = pi + 1
End If

' if node type a document
If nType = XmlNodeType.DocumentType Then
Console.WriteLine("Document:" +
textReader.Name.ToString())
dc = dc + 1
End If
' if node type is white space
If nType = XmlNodeType.Whitespace Then
Console.WriteLine("WhiteSpace:" +textReader.Name.ToString())
ws = ws + 1
End If
End While
Console.WriteLine("Total Comments:" + cc.ToString())
Console.WriteLine("Total Attributes:" + ac.ToString())
Console.WriteLine("Total Elements:" + el.ToString())
Console.WriteLine("Total Entity:" + et.ToString())
Console.WriteLine("Total Process Instructions:" + pi.ToString())
Console.WriteLine("Total Declaration:" + xd.ToString())
Console.WriteLine("Total DocumentType:" + dc.ToString())
Console.WriteLine("Total WhiteSpaces:" + ws.ToString())

End Sub
End Module

Writing XML Documents
The XmlWriter class contains the functionality to write to XML documents. It is an abstract base class used through XmlTextWriter and XmlNodeWriter classes. It contains methods and properties to write to XML documents. This class has many Writexxx method to write every type of item of an XML document. For example, WriteNode, WriteString, WriteAttributes, WriteStartElement, and WriteEndElement are some of them. Some of these methods are used in a start and end pair. For example, to write an element, you need to call WriteStartElement then write a string followed by WriteEndElement.

Besides many methods, this class has three properties. WriteState, XmlLang, and XmlSpace. The WriteState gets and sets the state of the XmlWriter class. 

Although it’s not possible to describe all the Writexxx methods here, let’s see some of them here.

First thing we need to do is create an instance of XmlTextWriter using its constructor. XmlTextWriter has three overloaded constructors, which can take a string, stream, or a TextWriter as an argument. We’ll pass a string (file name) as an argument, which we’re going to create. In my sample example, I create a file myXmlFile.xml in C:\\ dir.

' Create a new file in C:\\ dir
Dim textWriter As XmlTextWriter = New XmlTextWriter("C:\\myXmFile.xml",
Nothing)

After creating an instance, first thing you call us WriterStartDocument. When you’re done writing, you call WriteEndDocument and TextWriter’s Close method.
 
textWriter.WriteStartDocument()
 
……….. 
textWriter.WriteEndDocument()
textWriter.Close() 

The WriteStartDocument and WriteEndDocument methods open and close a document for writing. You must have to open a document before start writing to it. WriteComment method writes comment to a document. It takes only one string type of argument. WriteString method writes a string to a document. With the help of WriteString, WriteStartElement and WriteEndElement methods pair can be used to write an element to a document. The WriteStartAttribute and WriteEndAttribute pair writes an attribute.

WriteNode is more write method, which writes an XmlReader to a document as a node of the document. For example, you can use WriteProcessingInstruction and WriteDocType methods to write a ProcessingInstruction and DocType items of a document.

'Write the ProcessingInstruction node
Dim PI As String = "type='text/xsl' href='book.xsl'"
textWriter.WriteProcessingInstruction("xml-stylesheet", PI)

'Write the DocumentType node
textWriter.WriteDocType("book", Nothing, Nothing, "'softcover'>")

The below sample example summarizes all these methods and creates a new xml document with some items in it such as elements, attributes, strings, comments and so on. See Listing 5-14. In this sample example, we create a new xml file c:\xmlWriterText.xml. In this sample example, We create a new xml file c:\xmlWriterTest.xml using
XmlTextWriter: 

After that we add comments and elements to the document using Writexxx methods. After that we read our books.xml xml file using XmlTextReader and add its elements to xmlWriterTest.xml using XmlTextWriter. 

List 3. Writing an XML document using XmlTextWriter

' Import System.Xml namespace
Imports System.Xml

Module Module1

Sub Main()

' Create a new file in C:\\ dir
Dim textWriter As XmlTextWriter = New
XmlTextWriter("C:\\myXmFile.xml", Nothing)

' Opens the document
textWriter.WriteStartDocument()

' Write comments
textWriter.WriteComment("First Comment XmlTextWriter Sample
Example")
textWriter.WriteComment("myXmlFile.xml in root dir")


' Write first element
textWriter.WriteStartElement("Student")
textWriter.WriteStartElement("r", "RECORD", "urn:record")

' Write next element
textWriter.WriteStartElement("Name", "")
textWriter.WriteString("Student")
textWriter.WriteEndElement()

' Write one more element
textWriter.WriteStartElement("Address", "")
textWriter.WriteString("Colony")
textWriter.WriteEndElement()


' WriteChars
textWriter.WriteStartElement("Char")
Dim ch() As Char = {"b", "l", "last"}
textWriter.WriteChars(ch, 0, ch.Length)
textWriter.WriteEndElement()


' Ends the document.
textWriter.WriteEndDocument()
' close writer
textWriter.Close()

End Sub
End Module

Using XmlDocument
The XmlDocument class represents an XML document. This class provides similar methods and properties we’ve discussed earlier in this article.

Load and LoadXml are two useful methods of this class. A Load method loads XML data from a string, stream, TextReader or XmlReader. LoadXml method loads XML document from a specified string. Another useful method of this class is Save. Using Save method you can write XML data to a string, stream, TextWriter or XmlWriter. The following example in List 4 shows you how to use XmlDocument class’s Read, ReadXml and Save methods. 



You can also use Save method to display contents on console if you pass Console.Out as a parameter. For example: 

doc.Save(Console.Out) 

Here is one example of how to load an XML document using XmlTextReader. You read a file in an XmlTextReader and pass this in XmlDocument’s Load method to load the document.

Dim doc As New XmlDocument()
'Load the the document with the last book node.
Dim reader As New XmlTextReader("c:\\books.xml")
reader.Read()

doc.Load(reader)
doc.Save(Console.Out)

XmlDataDocument and DataSet
The XmlDataDocument is used to provide synchronization between DataSet and XML documents. You can use XmlDataDocument to read an XML document and generate a DataSet or read data from a DataSet and generate an XML file.


Viewing and Writing XML Data using ADO.NET DataSets

Abstract
Based on a real world project this article shows how to handle XML data in .NET using VB.NET DataSets and DataViews. The article presents step-by-step how to bind, read and view XML data and generate graphical reports with the build-in Crystal Reports engine. The article puts special emphasize on XML standardization and interoperability issues experienced during the project, and presents workarounds for existing limitations.

Introduction
This project was part of a larger project in the context of training acquisition and analysis. A Palm PDA is used for mobile data acquisition; these data are transferred onto a PC into a well-formed and valid XML data file. The task of this project was to develop a sophisticated data analysis and reporting application on the PC using the given XML data file.

The .NET framework was chosen for the PC application because of its great data handling capabilities, its powerful graphic functionality and the built-in reporting engine Crystal Reports. We selected the programming language VB.NET because we believe, that this is the language of the future under .NET – and because there is no major difference between the supported languages in VS.NET with respect to their offered functionality.

The idea was that both, the existing Palm application and the new VB.NET application can share one common XML file as its data source. 

XML Access Methods in .NET
.NET offers numerous XML-related classes for reading and writing XML documents. Although the documentation states, that any class conforms to the W3C XML 1.0 standard, there are trade-offs in respect to performance, efficiency, productivity, and XML compliance between the different implementations.

So we first investigated into these different access methods and then selected the most appropriate for our application. This step also addresses important issues that must be considered when dealing with legacy valid and well-formed XML data in .NET.

The VB.NET developer can use any of the following five methods to access XML data:

XmlTextReader
XmlValidatingReader
XmlDocument (and the rest of the DOM API),
XPathNavigator.

ADO.NET DataSets 
The detailed description of these methods would go beyond the scope of this article. Thus the following table summarizes very briefly the characteristics of each method and shows when to use which technology.



Table 1. XML Data Access Methods in .NET

When dealing with complex XML Schemas, then ADO.NET DataSet offers greatest support and flexibility. The DataSet, which can be used either relational databases or XML data files, is an in-memory cache of retrieved data. It is the major component for data handling in the ADO.NET architecture.

The DataSet provides a consistent relational programming model regardless of the data source; thus it consists of a collection of DataTable objects that you can relate to each other with DataRelation objects. A DataSet reads and writes data and schema as XML documents. The data and schema can be transported across HTTP and used by any application, on any platform that is XML-enabled.

Considering all these features and the fact that this VB.NET application will need to be WebService interoperable with a server application in future we decided to go for DataSet’s.

XML Conformance in .NET
Going with DataSets, ReadXML and ReadXMLSchema are the methods of choice to read the XML data, and the corresponding XML schema.

However doing so with the given valid and well-formed XML data file, raised the following runtime error: 

“The same table {description} cannot be the child table in two nested relations” 

Looking deeper into the file shows that the valid and well-formed (and thus fully W3C compliant) XML file had a child table called “description” which had more than one parent table. This part of the XML Schema is shown in Figure 1 . 



Figure 1. Original XML Schema

This is a valid and allowed design according to the W3C XML schema specification. (Refer: W3C definition of Parent child Relationship). Also the XMLSpy tool validated this XML schema as valid and well-formed.

More investigation showed that the .NET XML framework does not (yet?) support the full XML 1.0 standard. It does not support recursive schemas or designs where a child node type is beneath more than one parent node type. Of course, also all classes based on DataSets, like the System.Windows.Forms.DataGrid, which we were going to use, have the same restriction. In other words: 

XML Design Issue 1:  “.NET Datasets require that a table can only be a child of one other table.”

Microsoft has identified this as a bug (Q325695) and their knowledge base article [MS-Q325695] gives more information.
There are essentially three ways to solve this problem:

i) Write custom functions
ii) Write a converter
iii) Change your XML Schema 

If you can’t break change the existing XML Schema (for example because you are not the owner of the file), there are two ways to solve the problem: you customize all your ode, or you write a converter.

Customizing all your code means you write special methods for the XmlReader class and add records to a table with a hierarchical type design. You also have to implement a custom filtering mechanism if you want to display this data in bound controls, and a custom writing mechanism to write to the type of hierarchical format you are reading from. Even worse, any change in the schema file will cause changes in all of your

custom functions – so this isn’t really an.

The second option, if you can’t change the given XML Schema, is to write a converter. That means you define a new .NET compliant XML Schema for the given XML file and make use of the given DataSet capabilities. Then you write a bi-directional converter to convert the data from one XML Schema into the other before you use it. Although this still causes additional effort, it causes the least additional effort.
If you are the owner of the XML Schema file, then solution is to change the given the given XML Schema and make it also .NET compliant. In our situation, this meant, to define three different description type structures (Idescription, Ddecscription, Fdescription), for the three occurrences of the description.

To avoid multiple definitions of the same structure (which can cause maintenance errors in the future), we made use of the inheritance capabilities of XML Schema. We derived the three description types from the still existing base structure “description”. The details are shown in figure 2. 
 


Figure 2. Avoid one child with several parents

Reading XML Data with DataSets
VB.NET developers have several approaches of using a DataSet in the applications. This section explains the chosen approach to create a DataSet from the modified XML Schema, the tools that were used and how we populated the DataSet from the XML source file.

A DataSet can be typed or untyped. A typed DataSet is a dataset, which is created based a given XML Schema definition file (XSD file). Information from the schema (tables, columns, etc.) is generated and compiled into the new DataSet class as a set of first-class objects and properties.

Because a typed DataSet class inherits from the base DataSet class, the typed class assumes all of the functionality of the DataSet class and can be used with methods that take an instance of a DataSet class as a parameter

An untyped DataSet, in contrast, has no corresponding built-in schema. As in a typed dataset, an untyped DataSet contains tables, columns — but those are exposed only as collections. (However, after manually creating the tables and other data elements in an untyped DataSet, you can export the DataSet 's structure as a schema using the DataSet 's WriteXmlSchema method.)

You can use either type of DataSet in your application. However, Visual Studio has more tool support for typed DataSets, and they make programming with the DataSet much easier and less error-prone. So having considered all these options the decision was to go with ADO.NET typed DataSets.

Typed Datasets can be generated with the XSD.EXE tool, which is part of the VS.NET environment. The tool accepts a valid XML Schema file as input, as well as the language to use (C#, VB).  The following line shows a typical command line of the tool which uses the XML Schema file XSDSchemaFileName.xsd.

xsd.exe /d /l:VB.NET XSDSchemaFileName.xsd /n:XSDSchema.Namespace 

The /d directive tells the tool to generate DataSets, /l specifies the language to use, the optional /n defines the namespace to generate. The generated DataSet classes will be saved in the source file XSDSchemaFileName.vb.
Once the Typed DataSet classes are generated, the further procedure is almost a child’s play. The provided methods and properties guarantee data access in a type safe manner.

So the next step was to populate the Typed DataSet at runtime from the XML file. The ReadXml() and the WriteXml() methods of the typed DataSet class do this very easily  without any difficulty as the following to lines of code show: 

Dim myDS As New DataSet()
myDS.ReadXml("input.xml", XmlReadMode.ReadSchema)

Viewing XML Data
Having created and populated the DataSet the next step was to bind the data to the windows controls for user interaction. Since we were going to implement a grid view, this involved binding DataTables to DataGrids with user navigation facilities and providing parent-child relationships in the grid view so when a user selects a parent row, the corresponding child rows are to be shown automatically for editing purposes.

The DataTable is the primary building block of ADO.NET. The DataTable is made up of a DataColumn and DataRow collections. The columns define the schema of the DataTable and the rows make up the actual data in the DataTable. A DataView is a bindable, customized view of a DataTable. You can create multiple DataView's of the same DataTable, each one can contain different data sorted in different order.

Additionally, you can add, delete, or edit information in each DataView.
DataTable and DataView use the same instances of DataColumns, i.e. they share the common structure. However DataTable and DataView each have its own row collections. The DataTable is consists of DataRow's while the DataView is made up DataRowView's. Figure 3 shows the relationship between DataSet, DataTable and DataView.



Figure 3. Tables and Views 

The data binding of typed DataSets is very smooth in VB.NET. You can bind the data sources to the WindowsForms controls at design time or runtime without any difficulty. For example, you can specify which data column to be bound to a ComboBox  in a WindowsForm  by setting  the DataSource and DataMember property of that control so that it gets automatically bound at runtime.

We used the .NET DataView class to bind DataTables to the DataGrid. DataView supports data filtering and data sorting at design time or at runtime. For convenience a default DataView is generated for the developer for customization.
In the analysis part, the application needs to display some statistical information, which is calculated from several columns and which is shown in some dynamically generated extra columns as shown in Figure 4 .
 


Figure 4. The data view

Some of the data columns needed for these calculations were in different parent and child tables. Unfortunately it was not possible join two tables in the dataset  to show up in a single DataGrid.(for example, by using the DataSet.Relations property).To further clarify the situation  say I have two Parent & child tables Table1(id,columnA,columnB) and Table2(id,columnC,columnD)  and I want resulting datagrid columns to be view like (columnA , columnB , columnC , columnD). It is similar to the situation if I had wrote a sql query like

     select a.id , a.columnA , a.columnB , b.[columnC] , b.[columnD] 
     From Table1 a , Table2 b
     where a.id =b.id

It is important  to understand that DataView is not equivalent of a SQL VIEW. A Dataset does not contain a query processor and has no mechanism to perform a join.
 
The other way to do it (most developers seems to suggest) is to simply create a new datatable

That is typed the way you want it and manually moved the data from both  datatables to the new one. Also any data manipulation has to copy over to the original tables using a nested loops. This is a brute force method. It would be nice to here if anyone come up with something else.

Finally more references were added to the  schema file. But that is not the best workaround. The reason that the columns could not be merged is founded in the relational programming model of the DataSets. The existing XML file uses nested child tables instead of references. This is not (yet?) resolved by the .NET DataSets, so that references are missing to cross-reference tables and to establish relationships.

XML Design Issue 2:  “Use references in XML data files instead of nested child tables to show up a joined table  in a single DataGrid” 
So the next change was to introduce references where needed.

 

Figure 5. Adding references to tables
 
In our application we use the Date column as a filter criteria for the generated graphs in the Crystal Report Engine. Thus in Figure 5 a Date column has been added to the session table, which is a direct reference of the date column of the day table. 

Reporting XML Data
Having completed the data binding to Windows forms the next task was to generate the summarized reports in both graphical and tabular forms. We have chosen the Crystal Reports Engine (which is delivered with the VS.NET environment) with its powerful dynamic reporting capabilities.

Crystal Reports for Visual Studio .NET offers the following three object models:

>The Engine Object Model
>The Windows Forms Viewer Object Model
>The Web Forms Viewer Object Model 

The top level Engine object model is the report document object. The report document object offers sophisticated control over your object. It contains all the properties and methods needed to interface with and customize a report. How ever it does not provide the display of the reports.

This is where viewer objects and the report document objects come together. The Windows Forms viewer allows you to view a report in a Windows application and the Web Forms viewer does it for a Web application.

So we selected the Engine Object model together with the Windows Object Model to accomplish the requirements. The Vb.NET CrystalDecisions.Windows.Forms namespace in the CrystalDecisions.Windows.Forms.dll assembly provides support for the Windows Forms Viewer control and its associated classes. It can also dynamically update the report being hosted and interact with controls within a windows application.

When using the Crystal Report Engine you basically have two options for the selection of data source. The first one is passing the raw XML file, and the second is passing the populated DataSet to the Crystal Report Engine.
When using the first approach you have to verify on every usage, that the XML file contains correct data types (data validation). Otherwise Crystal Report Engine would interpret the data types in the XML files differently (e.g. float are used as strings), which prevents the data from being processed further within Crystal Reports.

The second, recommended option is passing the already populated typed DataSets to the Crystal Report Engine. This saves the runtime verification of data types. The content and the scope of the report is determined at runtime according to the user selection of the required report format. Crystal Reports provides template files for this. Figure 6 shows a sample report.

Figure 6. Crystal Report View

Summary
Visual Studio .NET has a very rich set of tightly integrated tools that work together to support C# application developer. For XML based applications it provides schema generation and validation tools. It offers the methods of choice for XML handling. Especially the DataSet classes make reading and writing of XML data extremely easy. The DataSet classes provide easy data binding to the GUI and to even third-party tools like Crystal Reports for advanced reporting.

Other very nice new features are the new source code documentation support and application installer production. The code documentation tool generates a very nice HTML documentation (they improved the JavaDoc style), which is completely XML based. The integrated application installer allows easy creation of a setup program for your application.

Despite the currently existing limitations of DataSets concerning its XML standard support, it is the tool of choice for XML data handling with .NET.
Once your XML data sources confirm to the “.NET XML standard” the rest can be handled without much difficulty. It offers lot of built-in powerful classes and methods for reading, writing and displaying data. This allows the developer to concentrate on the more important application logic rather than having to worry about data handling issues or framework specifics.

Generating XML from SQL Database

Description
This sample shows how you can obtain a Dataset from (in this case) a SQL Server database, and then write it out to an XML Document. As an "Extra Added Bonus(tm)", it can show you how to write the schema as well. The SQL Server database in question is the venerable and useful Northwind database.

The significant parameters are all declared at the beginning as strings, so that you can easily debug and tweak for your own usage. In this section, we create a connection string, a query string (in standard SQL) and specify a target filename.

Then, we call the function,

Shared Sub writeSQLQueryToFileAsXML(connString As String, query As String, filename As String)

This function first gets a connection, creates an adapter and specifies the connection and command to run through it, then obtains a dataset from that adapter.

This function then shows how you can write that DataSet out to a stream (in this case, a FileStream) as an XML document through the DataSet member, public void WriteXml(Stream). And if you've got some extra time on your hands, you can even uncomment the code in the last section, to write the DataSet's SCHEMA to a file!

An interesting corollary to this example would be to stream the DataSet's XML out to an HTTPResponse's HTTPWriter.outputStream, but that is both the subject of another sample, and also perhaps more easily done through native SQL Server capabilities.

Note that the XML that is written is output as a fragment, that is, there is no element, at the beginning, nor is there a unifying, single root node. While you will have to add both of these, this is really a sensible choice. First of all, only you, and your particular application context, can say what is the right name for the root node - is it , or ? Secondly, it may be a node or set of nodes buried down inside a much larger document, such as a node under one of several nodes. Get the picture?

Compiling and Running the code:

vbc XMLGenFromSQLDbPB.vb
Or
vbc /r:System.dll,System.Data.dll,System.Xml.dll  XMLGenFromSQLDbPB.vb
This will return you with the location where it had saved the XML file.You can specify it in the String s.

Source Code:
Imports System
Imports System.Data
Imports System.Xml
Imports System.Data.SqlClient
Imports System.IO

Namespace WriteXML
Public Class WriteXML

Shared Sub Main()

'*******************************************************************
' NOTE : YOU WILL NEED TO HAVE SQL SERVER (or MSDE) AVAILABLE, AND
' YOU WILL NEED THE NORTHWIND DATABASE INSTALLED IN THE SQL SERVER
' INSTANCE IN ORDER FOR THIS TO WORK.
'
' MODIFY THE FOLLOWING CONNECTION STRING AND QUERY STRING TO RUN
' THIS SAMPLE AGAINST A DIFFERENT DATABASE AND/OR QUERY.
'*******************************************************************

Dim outputFileName As String = "C:/myXmlData" ' ".xml" will be appended.
Dim connString As String = "user id=sa;password=password;" + "Database=northwind;server=(local);"
Dim sqlQueryString As String = "SELECT * FROM Suppliers"

' Here's the meat of the demonstration.
writeSQLQueryToFileAsXML(connString, sqlQueryString, outputFileName)

Console.WriteLine("Wrote query results to {0}", outputFileName)
End Sub 'Main


Shared Sub writeSQLQueryToFileAsXML(connString As String, query As String, filename As String)

Dim myConn As New SqlConnection(connString)

Dim adapter As New SqlDataAdapter()

adapter.SelectCommand = New SqlCommand(query, myConn)

' Build the DataSet
Dim myDs As New DataSet()

adapter.Fill(myDs)

Dim myFs As FileStream = Nothing

' Get a FileStream object
myFs = New FileStream(filename + ".xml", FileMode.OpenOrCreate, FileAccess.Write)
' Apply the WriteXml method to write an XML document
myDs.WriteXml(myFs)
' It is always good housekeeping to close a file.
myFs.Close()
End Sub 'writeSQLQueryToFileAsXML
End Class 'WriteXML '**************************************************
End Namespace 'WriteXML ' Uncomment the following code if you also want to
' dump the DataSet's schema to a file...
'***************************************************
'
' Get a FileStream object
'
myFs = new FileStream(filename+"_Schema.xml",
'
FileMode.OpenOrCreate,
'
FileAccess.Write);
'
myDs.WriteXmlSchema(myFs);
'
' It is always good housekeeping to close a file.
'
myFs.Close();


Inserting Data to an XML Document


The XmlNode and the XmlDocument classes can be used to insert XML data to an existing document or to a new document.

Adding namspace Reference
Since Xml classes are defined in the System.XML namespace, so first thing you need to do is to Add the System.XML reference to the project.

Imports  System.Xml

Loading XML to Document
LoadXml method of XmlDocument can be used to load XML data to a document or to load an existing XML document..

' Load XML data to a document
Dim  doc as  new XmlDocument();
doc.LoadXml("" +
" Old Data" +
"
")

Inserting XML Data
The below code inserts XML data to the file and saves file as InsertedDoc.xml.

Souce Code:  
Try
Dim currNode As XmlNode
Dim doc As New XmlDocument()
doc.LoadXml(("" + " Old Data" + ""))
Dim docFrag As XmlDocumentFragment = doc.CreateDocumentFragment()
docFrag.InnerXml = "" + " Inserted Data" + ""
' insert the availability node into the documentcurrNode = doc.DocumentElement.FirstChild;
currNode.InsertAfter(docFrag, currNode.LastChild)
'save the output to a filedoc.Save("InsertedDoc.xml");
Catch e As Exception
Console.WriteLine("Exception: {0}", e.ToString())
End Try

The output of the code looks like this -

-
-
  Old Data
-
  Inserted Data
 

 

 



Reading XML Files in VB.NET

The XmlReader and XmlTextReader classes are defined in the System.XML namespace.

The XmlTextReader class is derived from XmlReader class. The XmlTextReader class can be used to read the XML documents. The read function of this document reads the document until end of its nodes.

In this article, I will show you how to use XmlTextReader class to read an XML document and write data to the console.

Adding namspace Reference
Since Xml classes are defined in the System.XML namespace, so first thing you need to do is to Add the System.XML reference to the project.

Imports  System.Xml

Open an XML Document
The constructor of the XmlTextReader class opens an XML file. In this sample, I used an XML file called xmltest.xml in C\temp directory. You can download the attached file.

' Open an XML file
Dim reader As New XmlTextReader("C:\temp\xmltest.xml")

Reading Data
The Read method of the XmlTextReader class reads the data.

While reader.Read()
Console.WriteLine(reader.Name)
End While
Imports System
Imports System.Xml

Namespace ReadXML
End Namespace 'ReadXML
'
' Summary description for Class1.
'
public class Class1

Class1()
Dim Main As Integer
String()
If (True) Then
Try
' Open an XML file
Dim reader As New XmlTextReader("C:\temp\xmltest.xml")
While reader.Read()
Console.WriteLine(reader.Name)
End While

Catch e As Exception
Console.WriteLine("Exception: {0}", e.ToString())
End Try
Return 0
End If

Writing XML Files in VB.NET


The XmlWriter and XmlTextWriter classes are defined in the System.XML namespace.

The XmlTextWriter class is derived from XmlWriter class, which represents a writer that provides fast non-cached forward-only way of generating XML documents based on  the W3C Extensible Markup Language (XML) 1.0 specification.

In this article, I will show you how to use XmlTextWriter class to create an XML document and write data to the document.

Adding namspace Reference
Since Xml classes are defined in the System.XML namespace, so first thing you need to do is to Add the System.XML reference to the project.

Imports System.Xml

Creating an XML Document
The constructor of the XmlTextWriter class creates an XML file if file doesn't exist. In this sample, I create a new XML file called xmltest.xml in C\temp directory.

Dim writer As New XmlTextWriter("C:\temp\xmltest.xml", Nothing)

NOTE: If you don't want to write data in an XML file and want to display XML contents on the Console, pass Console.Out as a parameter of the constructor.
Dim writer As New XmlTextWriter(Console.Out)

Adding Data to the Document
The WriteStartDocument method starts a new document. The WriteStartElement and the WriteEndElement pair is used to add a new element to the document. The WriteString writes a string to the document.

writer.WriteStartDocument()
writer.WriteComment("Commentss: XmlWriter Test Program")
writer.WriteProcessingInstruction("Instruction", "Person Record")
writer.WriteStartElement("p", "person", "urn:person")
writer.WriteStartElement("LastName", "")
writer.WriteString("Chand")
writer.WriteEndElement()
writer.WriteElementInt16("age", "", 25)
writer.WriteEndDocument()

Imports System
Imports System.Xml

Namespace WriteToXML

'
' Summary description for Class1.
'

Public Class Class1

Public Sub New()
End Sub 'New

'Entry point which delegates to C-style main Private Function
Public Overloads Shared Sub Main()
System.Environment.ExitCode = Main(System.Environment.GetCommandLineArgs())
End Sub


Overloads Public Shared Function Main(args() As String) As Integer
Try
' Creates an XML file is not exist
Dim writer As New XmlTextWriter("C:\temp\xmltest.xml", Nothing)

' Starts a new document
writer.WriteStartDocument()
'Write comments
writer.WriteComment("Commentss: XmlWriter Test Program")
writer.WriteProcessingInstruction("Instruction", "Person Record")
' Add elements to the file
writer.WriteStartElement("p", "person", "urn:person")
writer.WriteStartElement("LastName", "")
writer.WriteString("Chand")
writer.WriteEndElement()
writer.WriteStartElement("FirstName", "")
writer.WriteString("Mahesh")
writer.WriteEndElement()
writer.WriteElementInt16("age", "", 25)

' Ends the document
writer.WriteEndDocument()
Catch e As Exception
Console.WriteLine("Exception: {0}", e.ToString())
End Try

Return 0
End Function 'Main
End Class 'Class1
End Namespace 'WriteToXML


Creating XML Documents with the XmlTextWriter Class

Introduction
As XML's popularity and use in Web-enabled applications continues to rise, it is becoming more and more important to have a good understanding of how to create, consume, and alter XML documents through .NET. In simplest terms, an XML file is really nothing more than a bulky text file, and prior to .NET, many ASP developers, when needing to generate an XML file on the fly, simply Response.Writed out the content of the XML document.

While Response.Write-ing an XML file works, it is far from ideal for a number of reasons. First, since it composes an XML document with strings, one has to worry about escaping those characters that are illegal in XML. There are a number of illegal characters that cannot appear in the text portion of an XML document. These are <, >, &, ", and '. When generating an XML document as a string, you have to manually search for these illegal characters and escape them. Second, with complex XML documents with many namespaces, attributes, and elements, the code necessary to Response.Write out the document can become cryptic.

Fortunately, the .NET Framework provides a class designed specifically to create XML documents, the System.Xml.XmlTextWriter class. By using this class to create XML documents you don't need to worry about illegal XML characters in the text portion of the XML document, and the end code is much cleaner. In this article we'll look at using the XmlTextWriter class to create XML documents on the fly.

The Basics of the XmlTextWriter Class
The XmlTextWriter class contains a number of methods that are useful for starting and completing an XML document and for adding elements and attributes to the XML document. The most important methods are:

WriteStartDocument() - you should call this method to start creating an XML document. This will create the first line in the XML document, specifying that the file is an XML document and its encoding.
WriteStartElement(string) - this method creates a new element in the XML document with the name specified by the string input parameter. (You can also specify a namespace as a second, optional string parameter.)
WriteElementString(name, text_value) - If you want to create an XML element with nothing but text content (i.e., no nested elements), you can use this method.
WriteAttributeString(name, value) - this method writes an attribute name and value to the current element.
WriteEndElement() - this method closes off the element created in the WriteStartElement(string) method call.
WriteEndDocument() - this method completes the writing of the XML document.
Close() - this method closes the underlying stream, writing the contents of the XML document to the specified file location.

To get started using the XmlTextWriter class you need to specify the file and encoding in the class's constructor. The encoding needs to be of the type System.Text.Encoding; some example encoding values are: System.Text.Encoding.ASCII, System.Text.Encoding.Unicode, and System.Text.Encoding.UTF8. Alternatively, you can specify in the constructor that the output of the XmlTextWriter class should be squirted out to a specified Stream.

Creating a Simple XML Document with XmlTextWriter
To demonstrate using the XmlTextWriter class let's create a simple XML document, saving it to a specified file location. This XML document will contain information about the current user visiting the page, and will have this structure:



URL referrer info
User agent referrer info
languages info


visitor's IP address
raw URL requested


(This XML file structure was chosen so that it would illustrate using all of the XmlTextWriter methods discussed in the previous section.)
The code needed to create this XML document through an ASP.NET Web page is shown below:

<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Text" %>
(script language="C#" runat="server")
void Page_Load(object sender, EventArgs e)
{
// Create a new XmlTextWriter instance
XmlTextWriter writer = new
XmlTextWriter(Server.MapPath("userInfo.xml"), Encoding.UTF8);

// start writing!
writer.WriteStartDocument();
writer.WriteStartElement("userInfo");

// Creating the element
writer.WriteStartElement("browserInfo");

if (Request.UrlReferrer == null)
writer.WriteElementString("urlReferrer", "none");
else
writer.WriteElementString("urlReferrer",
Request.UrlReferrer.PathAndQuery);

writer.WriteElementString("userAgent", Request.UserAgent);
writer.WriteElementString("userLanguages",
String.Join(", ", Request.UserLanguages));
writer.WriteEndElement();

// Creating the element
writer.WriteStartElement("visitInfo");
writer.WriteAttributeString("timeVisited", DateTime.Now.ToString());
writer.WriteElementString("ip", Request.UserHostAddress);
writer.WriteElementString("rawUrl", Request.RawUrl);
writer.WriteEndElement();

writer.WriteEndElement();
writer.WriteEndDocument();
writer.Close();
}


First, notice that the System.Xml and System.Text namespaces have been imported. The Page_Load event handler begins by creating a new XmlTextWriter instance, indicating that its content should be saved to the file userInfo.xml and that its encoding should be UTF8 (a translation of 16-bit unicode encoding into 8-bits). Note that for each element with nested elements a WriteStartElement(elementName) method is called, along with a matching WriteEndElement() after the inner content has been renderred. Furthermore, the WriteElementString(elementName, textValue) is used for those elements with just text content.

Emitting XML Content to the Browser Window Directly
The previous example demonstrates how to use the XmlTextWriter class to create an XML document and persist it to a file. While this may be precisely what you are after, oftentimes when creating an XML document in an ASP.NET Web page we want to emit the XML content's to the client requesting the Web page. While this could be done in the previous example by opening the userInfo.xml file after creating it and then Response.Write()ing its contents out, this approach is a bit of a hack.

A better approach is to have the results of the XmlTextWriter emitted directly to the output stream. This can be accomplished quite easily, by changing one line of code in the previous code sample. In the XmlTextWriter constructor, rather than specifying a file path, we can specify a Stream. Specifically, we want to specify Response.OutputStream. When doing this you will need to make another small change to the ASP.NET Web page. In the <@ Page ... > directive you need to indicate the page's MIME type as text/xml. If you don't do this, some browsers may think the data being sent is for a standard HTML Web page, and will attempt to format the XML document just as they would an HTML page (which will hide the XML elements and remove all whitespace).

The following code shows an abbreviated version of the previous code sample, with the changes in bold.
<@ Page ContentType="text/xml" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Text" %>
(script language="C#" runat="server")
void Page_Load(object sender, EventArgs e)
{
// Create a new XmlTextWriter instance
XmlTextWriter writer = new
XmlTextWriter(Response.OutputStream, Encoding.UTF8);

// start writing!
...
}

Notice that by viewing the live demo you are shown an XML document (even though you are visiting an ASP.NET Web page). This is the same XML document that, in the previous code sample, was saved to userInfo.xml.

Conclusion
In this article we examined how to create an XML document using the System.Xml.XmlTextWriter class of the .NET Framework. The XmlTextWriter class can persist its generated XML document to a file location or a specified stream, in a number of encodings. Using the XmlTextWriter class has many advantages over building up an XML document one string at a time. The main ones being more legible code and not needing to worry about escaping illegal XML characters.

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

Links to this post:

Create a Link

<< Home