DevCity.NET - http://devcity.net
Practical XML Usage
http://devcity.net/Articles/261/1/article.aspx
Gaidar Magdanurov
Gaidar Magdanurov is the editor-in-chief of VBStreets (www.vbstreets.ru) - Russian web site dedicated to Microsoft Visual Basic. He is also a Microsoft MVP and member of Russian Speakers Bureau, the founder and the leader of VBStreets .NET User Group, active member of Russian GotDotNet community and author of dozen of articles and. Gaidar has wide expirience in Web and Windows development and likes to share his knowledge. 
by Gaidar Magdanurov
Published on 8/20/2006
 

Practical XML Usage

This article is a small step-by-step guide for .NET/XML beginners. The sample application was written in Visual Basic .NET, but main aspects in the code are shown also in C#. This article can be considered as a reference of XML, schemas or XML transformation. The code was written in Visual Studio 2005 for .NET Framework 2.0.


Introduction

Introduction

A common task for a developer working for a large business is to build and connect applications that process data. The data could be generated by an application which was working in a production environment for years and sometimes it is really difficult to connect one application to another because any change in working code can ruin the production environment.

In such a situation the easiest solution to implement is a new tier between two applications which gets data from one application and converts it to a format acceptable by the other one.

The most widely distributed format for data in modern applications is XML and it is a must for any developer to have a good understanding of XML principles and XML-related technologies. But inside the application a developer works with simple types and objects. (Of course, when using the Microsoft .NET  platform developers deals only with objects because even "simple types" such as Integer, String or Boolean are objects). Serialization comes into play to represent XML data as objects and vice versa. To make sure that data comes in the necessary format, validation against XML schema can be used.

When an application should only receive data in one format and convert it to another it is not necessary to deserialize objects from XML and then XML transformations (XSLT) comes into play. In this article I would like to show a sample project that uses these technologies.

 

The Task

The Task

The task is to process XML data about famous band discographies in XML format (discography files): validate discography files against schema, transform them to HTML and different XML formats and allow the visual editing of those files. The sample discography file is presented below.

<?xml version="1.0" encoding="utf-8"?>
<discography band="The Beatles" firstYear="1963" lastYear="1964">
    <album>
        <title>Please Please Me</title>
        <year>1963</year>
        <label>Parlaphone</label>
        <songs>
            <song>I Saw Her Standing There</song>
            <song>Misery</song>
            <song>Anna (Go to Him)</song>
            <song>Chains</song>
            <song>Boys</song>
            <song>Ask Me Why</song>
            <song>Please Please Me</song>
            <song>Love Me Do</song>
            <song>P.S. I Love You</song>
            <song>Baby It's You</song>
            <song>Do You Want to Know a Secret</song>
            <song>A Taste of Honey</song>
            <song>There's a Place</song>
            <song>Twist and Shout</song>
        </songs>
    </album>
    <album>
        <title>With the Beatles</title>
        <year>1963</year>
        <label>Parlaphone</label>
        <songs>
            <song>It Won't Be Long</song>
            <song>All I've Got to Do</song>
            <song>All My Loving</song>
            <song>Don't Bother Me</song>
            <song>Little Child</song>
            <song>Till There Was You</song>
            <song>Please Mr. Postman</song>
            <song>Roll Over Beethoven</song>
            <song>Hold Me Tight</song>
            <song>You Really Got a Hold on Me</song>
            <song>I Wanna Be Your Man</song>
            <song>Devil in Her Heart</song>
            <song>Not a Second Time</song>
            <song>Money (That's What I Want)</song>
        </songs>
    </album>
</discography>

The output HTML file for this sample discography file should be as follows.

<html>
<head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>The Beatles</title>
</head>
<body>
    <h1>The Beatles - Discography (1963 - 1964)</h1>
    <h2>Please Please Me (Parlaphone, 1963)</h2>
    <ol>
        <li>I Saw Her Standing There</li>
        <li>Misery</li>
        <li>Anna (Go to Him)</li>
        <li>Chains</li>
        <li>Boys</li>
        <li>Ask Me Why</li>
        <li>Please Please Me</li>
        <li>Love Me Do</li>
        <li>P.S. I Love You</li>
        <li>Baby It's You</li>
        <li>Do You Want to Know a Secret</li>
        <li>A Taste of Honey</li>
        <li>There's a Place</li>
        <li>Twist and Shout</li>
    </ol>
    <h2>With the Beatles (Parlaphone, 1963)</h2>
    <ol>
        <li>It Won't Be Long</li>
        <li>All I've Got to Do</li>
        <li>All My Loving</li>
        <li>Don't Bother Me</li>
        <li>Little Child</li>
        <li>Till There Was You</li>
        <li>Please Mr. Postman</li>
        <li>Roll Over Beethoven</li>
        <li>Hold Me Tight</li>
        <li>You Really Got a Hold on Me</li>
        <li>I Wanna Be Your Man</li>
        <li>Devil in Her Heart</li>
        <li>Not a Second Time</li>
        <li>Money (That's What I Want)</li>
    </ol>
</body>
</html>

And the output XML file should be as in the following.

<?xml version="1.0" encoding="utf-8" ?>
<disco>
    <description>
        <name>The Beatles</name>
        <firstYear>1963</firstYear>
        <lastYear>1964</lastYear>
    </description>
    <albums>
        <album>
            <title>Please Please Me</title>
            <label>Parlaphone</label>
            <year>1963</year>
            <songs>
                <title>I Saw Her Standing There</title>
                <title>Misery</title>
                <title>Anna (Go to Him)</title>
                <title>Chains</title>
                <title>Boys</title>
                <title>Ask Me Why</title>
                <title>Please Please Me</title>
                <title>Love Me Do</title>
                <title>P.S. I Love You</title>
                <title>Baby It's You</title>
                <title>Do You Want to Know a Secret</title>
                <title>A Taste of Honey</title>
                <title>There's a Place</title>
                <title>Twist and Shout</title>
            </songs>
        </album>
        <album>
            <title>With the Beatles</title>
            <label>Parlaphone</label>
            <year>1963</year>
            <songs>
                <title>It Won't Be Long</title>
                <title>All I've Got to Do</title>
                <title>All My Loving</title>
                <title>Don't Bother Me</title>
                <title>Little Child</title>
                <title>Till There Was You</title>
                <title>Please Mr. Postman</title>
                <title>Roll Over Beethoven</title>
                <title>Hold Me Tight</title>
                <title>You Really Got a Hold on Me</title>
                <title>I Wanna Be Your Man</title>
                <title>Devil in Her Heart</title>
                <title>Not a Second Time</title>
                <title>Money (That's What I Want)</title>
            </songs>
        </album>
    </albums>
</disco>

I am very sorry for the lengthy listings but they should give you the idea of what are we going to get in the end.

 

 

First Steps to building the application

Step by step guide to building the desired application

Step 1. Create a schema for our discography file.
It is not a difficult job to create a schema for such a simple XML file but for more complex files the process of authoring a schema can take a long time. But with Visual Studio you don't have to worry! Open an XML file in Visual Studio and click on the XML > Create Schema menu item.

Then you see the schema automatically generated for the XML file. Right-click on the code and select View Designer to visually design the schema. In our particular situation we don't have to change anything and can just save the schema file for further use. The schema file is presented below.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="discography">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded" name="album">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="title" type="xs:string" />
                            <xs:element name="year" type="xs:int" />
                            <xs:element name="label" type="xs:string" />
                            <xs:element name="songs">
                                <xs:complexType>
                                    <xs:sequence>
                                            <xs:element maxOccurs="unbounded" name="song" type="xs:string" />
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="band" type="xs:string" use="required" />
        <xs:attribute name="firstYear" type="xs:int" use="required" />
        <xs:attribute name="lastYear" type="xs:int" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

Step 2. Create a class corresponding to XML file
Now we have to create a class file to have an ability to serialize/deserialize objects to and from XML files. For sure, we can do some coding but I prefer to use tools to generate class files automatically. To do this we should go to command-line and use xsd.exe utility which is available in .NET Framework SDK. You can use shortcut SDK Command Prompt from Microsoft .NET Framework SDK folder in Start menu. In SDK Command Prompt type:

xsd.exe /c /l:vb path_to_schema_file

Option /c indicates that we want xsd.exe to generate a serializable class file and not a typed data-set file.  Option /l:vb tells the utility to generate Visual Basic class file (to create a C# file use /l:cs option). By default, the generated class file will be placed to the same directory in which the schema file resides.

The listing of the enerated class file is the following.

Option Strict Off
Option Explicit On

Imports System.Xml.Serialization

<SYSTEM.CODEDOM.COMPILER.GENERATEDCODEATTRIBUTE("XSD", _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=true), _
System.Xml.Serialization.XmlRootAttribute([Namespace]:="", IsNullable:=false)> _
Partial Public Class discography

    Private albumField() As discographyAlbum
    Private bandField As String
    Private firstYearField As Integer
    Private lastYearField As Integer

    <SYSTEM.XML.SERIALIZATION.XMLELEMENTATTRIBUTE("ALBUM")>_
    Public Property album() As discographyAlbum()
        Get
            Return Me.albumField
        End Get
        Set
            Me.albumField = value
        End Set
    End Property

    <SYSTEM.XML.SERIALIZATION.XMLATTRIBUTEATTRIBUTE()>_
    Public Property band() As String
        Get
            Return Me.bandField
        End Get
        Set
            Me.bandField = value
        End Set
    End Property

    <SYSTEM.XML.SERIALIZATION.XMLATTRIBUTEATTRIBUTE()>_
    Public Property firstYear() As Integer
        Get
            Return Me.firstYearField
        End Get
        Set
            Me.firstYearField = value
        End Set
    End Property

    <SYSTEM.XML.SERIALIZATION.XMLATTRIBUTEATTRIBUTE()>_
    Public Property lastYear() As Integer
        Get
            Return Me.lastYearField
        End Get
        Set
            Me.lastYearField = value
        End Set
    End Property
End Class


<SYSTEM.CODEDOM.COMPILER.GENERATEDCODEATTRIBUTE("XSD", _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=true)> _
Partial Public Class discographyAlbum

    Private titleField As String
    Private yearField As Integer
    Private labelField As String
    Private songsField() As String

    Public Property title() As String
        Get
            Return Me.titleField
        End Get
        Set
            Me.titleField = value
        End Set
End Property

    Public Property year() As Integer
        Get
            Return Me.yearField
        End Get
        Set
            Me.yearField = value
        End Set
    End Property

    Public Property label() As String
        Get
            Return Me.labelField
        End Get
        Set
            Me.labelField = value
        End Set
    End Property

    <SYSTEM.XML.SERIALIZATION.XMLARRAYITEMATTRIBUTE("SONG", IsNullable:="false)">_
    Public Property songs() As String()
        Get
            Return Me.songsField
        End Get
        Set
            Me.songsField = value
        End Set
    End Property
End Class

 

Final steps

Step 3. Create XSLT files for desired transformations
Talking about XSLT language is out of  scope of this article, so I just present one of XSLT files listings below.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="/">
    <html>
    <head>
        <title>
            <xsl:value-of select="//@band"/>
        </title>
    </head>
<body>
    <h1>
        <xsl:value-of select="//@band"/> - Discography (<xsl:value-of select="//@firstYear"/> - <xsl:value-of select="//@lastYear"/>)
    </h1>
    <xsl:for-each select="//album">
        <h2>
            <xsl:value-of select="./title"/> (<xsl:value-of select="./label"/>, <xsl:value-of select="./year"/>)
        </h2>
    <ol>
        <xsl:for-each select="./songs/song">
        <li>
            <xsl:value-of select="."/>
        </li>
        </xsl:for-each>
    </ol>
    </xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Step 4. Join all parts together
Now it is time to create a simple application. I don't think it is necessary to discuss the application. If you wish to play with the sample you can download the attached archive and look though the code yourself. Below I will dwell only on the crucial points.

Validating an XML file

The easiest way to validate an XML file against the schema is to use the XmlDocument class that has the Validate method which in its turn uses a callback function to notify  found errors. If it is not necessary to get information about validation error the callback function can set some flag to show that a given file is not valid.

'''''' Class field
Private isValid As Boolean = true

'''''' The code for validation
Dim xml As New XmlDocument()
xml.Load(xmlPath)
xml.Schemas.Add(Nothing, schemaPath)
xml.Validate(AddressOf ValidationHandler)
If isValid Then
    ''' XML file is valid
Else
    ''' XML file is invalid
End If

'''''' Callback method
Private Sub ValidationHandler(ByVal sender As Object, ByVal e As ValidationEventArgs)
    isValid = False
End Sub

It is important to validate XML files before performing transformations or using deserialization to objects because invalid XML files will result in run-time errors.

Performing a transformation

To transform an XML file using XSLT presented above we will use XslCompiledTransform which resides in System.Xml.Xsl namespace.

Dim xml As New XslCompiledTransform()
xml.Load(xsltPath)
xml.Transform(xmlPath, outputPath)

Serializing/Deserializing objects

If you look through the System.Xml.Serialization namespace you will find a class XmlSerializer which makes ut very easy to carry out serialization/deserialization.

Deserialization

Private disco As discography
Dim xml As New XmlSerializer(Type.GetType("SimpleXSLT.discography"))
Dim xmlFileStream, As New FileStream(xmlPath, FileMode.Open, FileAccess.Read)
disco = CType(xml.Deserialize(xmlFileStream), discography)
xmlFileStream.Close()

Serialization

Dim disco As New discography
'''' set disco properties here

Dim xml As New XmlSerializer(Type.GetType("SimpleXSLT.discography"))
Dim xmlFileStream As New FileStream(xmlPath, FileMode.Create, FileAccess.Write)
xml.Serialize(xmlFileStream, disco)
xmlFileStream.Close()

 

Conclusions

Conclusion

At the end we get something like shown in the figure below.

This is just a very simple application built to be an example of what we were talking about above. In the archive attached to the article you will find the full source code of the sample application in the SimpleXSLT folder and XML files, schemas and XSLT files in the the Input folder. I hope you got the idea and will go through the documentation for the  classes and methodsI ahve mentioned. Using the techniques shown you can work with XML with enjoyment. Good luck!