perfectxml.com
 Basic Search  Advanced Search   
Topics Resources Free Library Software XML News About Us
home » info bank » articles » Working with XML in .NET Part I Sat, Feb 23, 2008
Working with XML in .NET Part I

Abstract: The XML support in .NET is provided via five namespaces, System.Xml, System.Xml.XPath, System.Xml.Xsl, System.Xml.Schema, and System.Xml.Serialization. These namespaces are part of the System.Xml.dll assembly. The classes in these namespaces are designed to support XML 1.0, XML Namespaces, XPath, XSLT, DOM, and XSD Schema W3C standards. The .NET Framework does not support SAX, instead supports a different, "pull" based streaming API.

In this article you'll learn about basics of XML processing in .NET. More specifically, this article includes Visual Basic .NET, C# .NET, and C++ .NET code examples to illustrate:
  • writing XML,
  • parsing XML,
  • manipulating XML,
  • querying XML,
  • transforming XML,
  • validating XML, and
  • serializing XML.
For simplicity, most of the applications presented in this article are console applications.

In this first part, we'll see examples of creating XML documents from scratch.

Author: perfectxml.com Team
Last Updated: February 16, 2003
Download: XMLNetPart1.zip (39 KB ZIP file)


Writing XML
The .NET Framework supports two approaches for creating the XML documents. The first method involves using the XmlTextWriter class to create a stream of XML data; and the alternative method is to use the XmlDocument DOM-implementation class to create an in-memory representation of the XML document.

XmlTextWriter is very efficient and requires less system resources when compared to XmlDocument; however XmlTextWriter is limited to forward-only, non-cached way of generating XML document. With XmlTextWriter, once you have written to the output stream, there is no way to access it or change it. It does not cache anything in memory, the generated XML text is directly sent to the stream.

XmlDocument, on the other side, holds the entire XML document in memory, as it is being created, and provides random access and updates to any part of the XML document. However, as the entire XML document is loaded into and then unloaded from the memory, XmlDocument might not be best solution for huge XML documents, where you only need to generate and save/stream the XML document, and don't need random access. In such cases, XmlTextWriter class is preferred.

So, let's start with a sample application that illustrates using XmlTextWriter to create an XML document from scratch.

Using XmlTextWriter Class
In this section you'll see learn about creating XML documents from scratch. First, we'll look at Visual Basic .NET code, and then we'll write the same sample application using C# and Managed C++.

Let's write a console application that takes three command line parameters: the directory name (for example: c:\my documents), the file extension (for example: doc), and the output XML file name (for example: c:\output.xml). This sample application will look for files with the specified extension in the specified directory, and generate the XML document that contains the details (such as size, attributes, created date, etc.) for the matching files.

Visual Basic .NET (WritingXML.vb):
Imports System
Imports System.IO
Imports System.Xml

Namespace PerfectXML.VB.Samples.Basics
 Module WritingXML

  Sub Main()
   Dim strCmdLineArray As String()

   Try
    strCmdLineArray = Environment.GetCommandLineArgs()
    If strCmdLineArray.Length < 4 Then
     PrintUsage()
     Return
    End If

    'Command Line Arguments:
    '   1: The directory path 
    '   2: File Extension
    '   3: Output XML filename with full path
    '
    'Output: Creates the Output XML file containing file details
    'such as size, attributes, Modified date, etc.

    'Step 1. Get the file list having specified extension from the specified directory 
    Dim DirInfo As New DirectoryInfo(strCmdLineArray(1))
    Dim DirFiles As FileInfo() = DirInfo.GetFiles("*." & strCmdLineArray(2))

    'Step 2. Start creating the XML document file
    Dim OutputXMLWriter As New XmlTextWriter(strCmdLineArray(3), Text.Encoding.UTF8)
    OutputXMLWriter.Formatting = Formatting.Indented

    'Step 2a: Start Document
    OutputXMLWriter.WriteStartDocument()

    'Step 2b: Root Element
    OutputXMLWriter.WriteStartElement("ns", "FileDetails", "uri:MyFiles")
    OutputXMLWriter.WriteAttributeString("ns", "DirPath", "uri:MyFiles", strCmdLineArray(1))
    OutputXMLWriter.WriteAttributeString("ns", "ExtensionFilter", "uri:MyFiles", strCmdLineArray(2))
    OutputXMLWriter.WriteAttributeString("ns", "MatchingFiles", "uri:MyFiles", DirFiles.Length)

    'Step 2c: For each file, create the File Details nodes
    Dim ASingleFile As FileInfo
    For Each ASingleFile In DirFiles
     OutputXMLWriter.WriteStartElement("ns", "File", "uri:MyFiles")

     OutputXMLWriter.WriteElementString("Name", "uri:MyFiles", ASingleFile.Name)
     OutputXMLWriter.WriteElementString("Size", "uri:MyFiles", ASingleFile.Length)
     OutputXMLWriter.WriteElementString("Created", "uri:MyFiles", ASingleFile.CreationTime)
     OutputXMLWriter.WriteElementString("LastAccessed", "uri:MyFiles", ASingleFile.LastAccessTime)
     OutputXMLWriter.WriteElementString("LastModified", "uri:MyFiles", ASingleFile.LastWriteTime)
     OutputXMLWriter.WriteElementString("Attributes", "uri:MyFiles", ASingleFile.Attributes.ToString())

     OutputXMLWriter.WriteEndElement()
    Next

    'Step 2d: End the Root element, and the document
    OutputXMLWriter.WriteEndElement()
    OutputXMLWriter.WriteEndDocument()

    'Step 3: Write to the XML file and close the Writer
    OutputXMLWriter.Flush()
    OutputXMLWriter.Close()

    'Finish
    Console.Write("Output saved in file: " & strCmdLineArray(3))

   Catch err As Exception
    Console.Write("Error: " & err.Message)
   End Try
   Console.Write(vbNewLine & "Press Enter to continue...")
   Console.ReadLine()
  End Sub

  Sub PrintUsage()
   Console.WriteLine("Usage: " & vbNewLine & vbTab & _
    "VB_WritingXML.exe DirPath FileExtension OutputXMLFileName" & vbNewLine)
   Console.WriteLine("Example: " & vbNewLine & vbTab & _
    "VB_WritingXML.exe ""c:\Personal\My Documents"" doc c:\Personal\DailyDocs.xml" & _
    vbNewLine & vbTab & _
    "VB_WritingXML.exe c:\Office * c:\Personal\AllOfficeFiles.xml")
   Console.Write(vbNewLine & "Press Enter to continue...")
   Console.ReadLine()
  End Sub

 End Module
End Namespace
When the above console application is executed by passing the command line parameters as:
		VB_WritingXML.exe c:\winnt\system32   dll   c:\CurrentDLLs.xml
it generates the output XML file similar to one shown in the following screenshot:



The above Visual Basic .NET code:
  1. calls Environment.GetCommandLineArgs to get the string array containing command line argument values, and then makes sure that all the three required parameters are passed, if not, prints the usage syntax text, and exits the application.

  2. creates an instance of DirectoryInfo passing it the value of first argument (the directory name, for example: c:\winnt\system32); and then calls the GetFiles method on the DirectoryInfo by providing the file extension (the second command line argument, for example, dll). Now we have a list of files with specific extension from the specified folder.

  3. creates an instance of XmlTextWriter passing it the value of the third argument (the output XML file name, such as c:\CurrentDlls.xml). This is where, we start creating the XML document, by calling methods such as WriteStartDocument, WriteStartElement, WriteAttributeString, WriteEndElement, and WriteEndDocument.

    After we end the XML document by calling WriteEndDocument, it is persisted into the the file (OutputXMLWriter.Flush) and the XmlTextWriter handle is closed.
Download the sample code ZIP file, open and build the VB_WritingXML.sln solution, run it by passing the command line parameters, and look at the generated XML file. It should look similar to one shown in the above screenshot. Note, how the namespaces are used in the above sample.

Let's look at the equivalent C# code. The following C# code does the exact same thing as the above VB .NET application.

C# .NET (WritingXML.cs):
using System;
using System.IO;
using System.Xml;

namespace PerfectXML.CS.Samples.Basics
{
  class WritingXML
  {
    [STAThread]
    static void Main(string[] args)
    {
      if (args.Length < 3)
      {
        PrintUsage();
        return;
      }

      try
      {
        //Step 1. Get the file list having specified extension from the specified directory 
        DirectoryInfo DirInfo = new DirectoryInfo(args[0]);
        FileInfo[] DirFiles  = DirInfo.GetFiles("*." + args[1]);

        //Step 2. Start creating the XML document file
        XmlTextWriter OutputXMLWriter = new XmlTextWriter(args[2], System.Text.Encoding.UTF8);
        OutputXMLWriter.Formatting = Formatting.Indented;

        //Step 2a: Start Document
        OutputXMLWriter.WriteStartDocument();

        //Step 2b: Root Element
        OutputXMLWriter.WriteStartElement("ns", "FileDetails", "uri:MyFiles");
        OutputXMLWriter.WriteAttributeString("ns", "DirPath", "uri:MyFiles", args[0]);
        OutputXMLWriter.WriteAttributeString("ns", "ExtensionFilter", "uri:MyFiles", args[1]);
        OutputXMLWriter.WriteAttributeString("ns", "MatchingFiles", "uri:MyFiles", DirFiles.Length.ToString());

        //Step 2c: For each file, create the File Details nodes
        foreach (FileInfo ASingleFile in DirFiles)
        {
          OutputXMLWriter.WriteStartElement("ns", "File", "uri:MyFiles");

          OutputXMLWriter.WriteElementString("Name", "uri:MyFiles", ASingleFile.Name);
          OutputXMLWriter.WriteElementString("Size", "uri:MyFiles", ASingleFile.Length.ToString());
          OutputXMLWriter.WriteElementString("Created", "uri:MyFiles", ASingleFile.CreationTime.ToString());
          OutputXMLWriter.WriteElementString("LastAccessed", "uri:MyFiles", ASingleFile.LastAccessTime.ToString());
          OutputXMLWriter.WriteElementString("LastModified", "uri:MyFiles", ASingleFile.LastWriteTime.ToString());
          OutputXMLWriter.WriteElementString("Attributes", "uri:MyFiles", ASingleFile.Attributes.ToString());

          OutputXMLWriter.WriteEndElement();
        }

        //Step 2d: End the Root element, and the document
        OutputXMLWriter.WriteEndElement();
        OutputXMLWriter.WriteEndDocument();

        //Step 3: Write to the XML file and close the Writer
        OutputXMLWriter.Flush();
        OutputXMLWriter.Close();

        //Finish
        Console.Write("Output saved in file: " + args[2]);
      }
      catch(Exception err)
      {
        Console.Write("Error: " + err.Message);
      }
            Console.Write("\nPress Enter to continue...");
            Console.ReadLine();
    }

    static void PrintUsage()
    {
      Console.WriteLine("Usage: \n\t" +
        "CSharp_WritingXML.exe DirPath FileExtension OutputXMLFileName\n");

      Console.WriteLine("Example: \n\t" +
        @"CSharp_WritingXML.exe ""c:\Personal\My Documents"" doc c:\Personal\DailyDocs.xml" +
        "\n\t" +
        @"CSharp_WritingXML.exe c:\Office * c:\Personal\AllOfficeFiles.xml");

      Console.Write("\nPress Enter to continue...");
      Console.ReadLine();
    }
  }
}

Finally, let's look at the equivalent managed C++ code. The following C++ code does the exact same thing as the above VB .NET and C# applications.

Managed C++ (CPP_WritingXML.cpp):
#include "stdafx.h"
#include <tchar.h>

#using <System.dll>
#using <mscorlib.dll>
#using <System.Xml.dll>

using namespace System;
using namespace System::IO;
using namespace System::Xml;

namespace PerfectXMLCPPSamplesBasics
{
  __gc public class WritingXML
  {
  public:
    void PrintUsage()
    {
      Console::WriteLine("Usage: \n\t" 
        "CPP_WritingXML.exe DirPath FileExtension OutputXMLFileName\n");

      Console::WriteLine("Example: \n\t" 
        "CPP_WritingXML.exe \"c:\\Personal\\My Documents\" doc c:\\Personal\\DailyDocs.xml" 
        "\n\t" 
        "CPP_WritingXML.exe c:\\Office * c:\\Personal\\AllOfficeFiles.xml");

      Console::Write("\nPress Enter to continue...");
      Console::ReadLine();
    }

    void BuildXML(String* DirPath, String* FileExt, String* OutputXMLFileName) 
    {
      try
      {
        //Step 1. Get the file list having specified extension from the specified directory 
        DirectoryInfo* DirInfo = new DirectoryInfo(DirPath);
        FileInfo* DirFiles[]  = DirInfo->GetFiles(String::Concat("*.", FileExt));

        //Step 2. Start creating the XML document file
        XmlTextWriter* OutputXMLWriter = new XmlTextWriter(OutputXMLFileName, System::Text::Encoding::UTF8);
        OutputXMLWriter->Formatting = Formatting::Indented;

        //Step 2a: Start Document
        OutputXMLWriter->WriteStartDocument();

        //Step 2b: Root Element
        OutputXMLWriter->WriteStartElement("ns", "FileDetails", "uri:MyFiles");
        OutputXMLWriter->WriteAttributeString("ns", "DirPath", "uri:MyFiles", DirPath);
        OutputXMLWriter->WriteAttributeString("ns", "ExtensionFilter", "uri:MyFiles", FileExt);
        OutputXMLWriter->WriteAttributeString("ns", "MatchingFiles", "uri:MyFiles", 
           DirFiles.Length.ToString());

        //Step 2c: For each file, create the File Details nodes
        int iFilesCount = DirFiles->Length;
        for(int iIndex=0; iIndex < iFilesCount; iIndex++)
        {
          FileInfo* ASingleFile = static_cast<FileInfo*> (DirFiles->Item[iIndex]);
          
          OutputXMLWriter->WriteStartElement("ns", "File", "uri:MyFiles");

          OutputXMLWriter->WriteElementString("Name", "uri:MyFiles", ASingleFile->Name);
          OutputXMLWriter->WriteElementString("Size", "uri:MyFiles", ASingleFile->Length.ToString());
          OutputXMLWriter->WriteElementString("Created", "uri:MyFiles", ASingleFile->CreationTime.ToString());
          OutputXMLWriter->WriteElementString("LastAccessed", "uri:MyFiles", 
             ASingleFile->LastAccessTime.ToString());
          OutputXMLWriter->WriteElementString("LastModified", "uri:MyFiles", 
             ASingleFile->LastWriteTime.ToString());
          OutputXMLWriter->WriteElementString("Attributes", "uri:MyFiles", 
             Convert::ToString(ASingleFile->Attributes));

          OutputXMLWriter->WriteEndElement();
        }

        //Step 2d: End the Root element, and the document
        OutputXMLWriter->WriteEndElement();
        OutputXMLWriter->WriteEndDocument();

        //Step 3: Write to the XML file and close the Writer
        OutputXMLWriter->Flush();
        OutputXMLWriter->Close();

        //Finish
        Console::Write("Output saved in file: {0}", OutputXMLFileName);
      }
      catch(Exception* err)
      {
        Console::Write("Error: {0}", err->Message);
      }

      Console::Write("\nPress Enter to continue...");
      Console::ReadLine();
    }
  
  };//End WritingXML Class
}//End Namespace

int _tmain(int argc, char* argv[])
{
  using namespace PerfectXMLCPPSamplesBasics;
  WritingXML* XMLHelper = new WritingXML();
  if(argc < 4)
  {
    XMLHelper->PrintUsage();
    return -1;
  }

  XMLHelper->BuildXML(argv[1], argv[2], argv[3]);

  return 0;
}

Using XmlDocument Class
The above sample application illustrated creating XML documents using the XmlTextWriter; now in this section, you'll learn about an alternate method of creating XML documents, by using the XmlDocument class. The XmlDocument class implements the W3C DOM Level 1 core and DOM Level 2 standards, and can be used to create and manipulate the XML documents.

The following sample application uses XmlDocument class to create an XML document. The generated XML document contains information about Windows Services on the current machine. Once again, we'll first look at the VB .NET code, then C# and finally managed C++ code.

Visual Basic .NET (WritingXMLUsingDOM.vb):
Imports System
Imports System.Xml
Imports System.ServiceProcess

Namespace PerfectXML.VB.Samples.Basics
 Module WritingXMLUsingDOM
  Public OutputFileName As String = "c:\Services.xml"

  Sub Main()
   Try
    Dim XMLDoc As New XmlDocument()
    Dim RootElem As XmlElement
    Dim SvcElem As XmlElement

    'Step 1: Create XML Declaration line and the Root Element
    XMLDoc.AppendChild(XMLDoc.CreateXmlDeclaration("1.0", "UTF-8", "no"))

    RootElem = XMLDoc.CreateElement("ns", "WindowsServices", "uri:MyServices")
    XMLDoc.AppendChild(RootElem)


    'Step 2: Get a list of services on the local computer
    Dim WinSvcsList() As ServiceController
    Dim AService As ServiceController
    WinSvcsList = ServiceController.GetServices()

    'Step 3: For each service
    For Each AService In WinSvcsList
     'Step 3a: Create SvcDetails child element
     SvcElem = XMLDoc.CreateElement("ns", "SvcDetails", "uri:MyServices")

     'Step 3b: Create attributes
     SvcElem.SetAttribute("Name", "uri:MyServices", AService.DisplayName)
     SvcElem.SetAttribute("Status", "uri:MyServices", AService.Status.ToString())

     'Step 3c: Append SvcDetails child element under the root element
     RootElem.AppendChild(SvcElem)
    Next

    'Step 4: Save the XML document
    XMLDoc.Save(OutputFileName)
    
    Console.WriteLine("XML Document saved as " & OutputFileName)
   
   Catch err As Exception
    Console.WriteLine("Err: " & err.Message)
   
   Finally
    Console.Write(vbNewLine & "Press Enter to continue...")
    Console.ReadLine()
   
   End Try
  End Sub
 End Module
End Namespace

When the above application is executed, it creates the output XML file (c:\Services.xml), which looks like:



The above Visual Basic .NET code:
  1. uses the classes in the System.ServiceProcess namespace to get the Windows Services details. Remember to add reference to System.ServiceProcess.dll.

  2. creates an instance of class XmlDocument, and first thing it does is to create the XML declaration line (<?xml ...?> processing instruction) and appends that to the document.

  3. creates the root element, gets the list of Windows Services, and for each entry, creates child element and attributes and appends the element to the root element.

  4. Finally, the XmlDocument Save method is called to saved the in-memory XML document into the disk file.
Let's look at equivalent C# code:

C# .NET (WritingXMLUsingDOM.cs):
using System;
using System.Xml;
using System.ServiceProcess;

namespace PerfectXML.VB.Samples.Basics
{
 class WritingXMLUsingDOM
 {
  public static string OutputFileName = @"c:\Services.xml";
  
  [STAThread]
  static void Main(string[] args)
  {
   try
   {
    XmlDocument XMLDoc = new XmlDocument();
    XmlElement RootElem, SvcElem;

      //Step 1: Create XML Declaration line and the Root Element
      XMLDoc.AppendChild(XMLDoc.CreateXmlDeclaration("1.0", "UTF-8", "no"));

      RootElem = XMLDoc.CreateElement("ns", "WindowsServices", "uri:MyServices");
      XMLDoc.AppendChild(RootElem);


      //Step 2: Get a list of services on the local computer
      ServiceController[] WinSvcsList;
      WinSvcsList = ServiceController.GetServices();

      //Step 3: For each service
      foreach (ServiceController AService in WinSvcsList)
      {
        //Step 3a: Create SvcDetails child element
        SvcElem = XMLDoc.CreateElement("ns", "SvcDetails", "uri:MyServices");

        //Step 3b: Create attributes
        SvcElem.SetAttribute("Name", "uri:MyServices", AService.DisplayName);
        SvcElem.SetAttribute("Status", "uri:MyServices", AService.Status.ToString());

        //Step 3c: Append SvcDetails child element under the root element
        RootElem.AppendChild(SvcElem);
      }

      //Step 4: Save the XML document
      XMLDoc.Save(OutputFileName);
      Console.WriteLine("XML Document saved as " + OutputFileName);
   }
   catch(Exception err)
   {
      Console.WriteLine("Err: " + err.Message);
   }
   finally
   {
      Console.Write("\nPress Enter to continue...");
      Console.ReadLine();
   }
  }
 }
}

And here is the managed C++ version of the above sample application:

Managed C++ (CPP_WritingXMLUsingDOM.cpp):
#include "stdafx.h"
#include <tchar.h>

#using <System.dll>
#using <mscorlib.dll>
#using <System.Xml.dll>
#using <System.ServiceProcess.dll>

using namespace System;
using namespace System::IO;
using namespace System::Xml;
using namespace System::ServiceProcess;

namespace PerfectXMLCPPSamplesBasics
{
   __gc public class WritingXMLUsingDOM
   {
   public:
   static String* OutputFileName = "c:\\Services.xml";

   void BuildXML() 
   {
      try
      {
       XmlDocument *XMLDoc = new XmlDocument();
       XmlElement *RootElem, *SvcElem;

       //Step 1: Create XML Declaration line and the Root Element
       XMLDoc->AppendChild(XMLDoc->CreateXmlDeclaration("1.0", "UTF-8", "no"));

       RootElem = XMLDoc->CreateElement("ns", "WindowsServices", "uri:MyServices");
       XMLDoc->AppendChild(RootElem);

       //Step 2: Get a list of services on the local computer
       ServiceController *WinSvcsList[] = ServiceController::GetServices();

       int iSvcsCount = WinSvcsList->Length;

       //Step 3: For each service
       for(int iIndex=0; iIndex < iSvcsCount; iIndex++)
       {
          ServiceController* AService = static_cast<ServiceController*>(WinSvcsList->Item[iIndex]);

          //Step 3a: Create SvcDetails child element
          SvcElem = XMLDoc->CreateElement("ns", "SvcDetails", "uri:MyServices");

          //Step 3b: Create attributes
          SvcElem->SetAttribute("Name", "uri:MyServices", AService->DisplayName);
          SvcElem->SetAttribute("Status", "uri:MyServices", Convert::ToString(AService->Status));

          //Step 3c: Append SvcDetails child element under the root element
          RootElem->AppendChild(SvcElem);
        }

        //Step 4: Save the XML document
        XMLDoc->Save(OutputFileName);
        Console::WriteLine("XML Document saved as {0}", OutputFileName);
      }
      catch(Exception* err)
      {
        Console::Write("Error: {0}", err->Message);
      }
      
      Console::Write("\nPress Enter to continue...");
      Console::ReadLine();
   }
   
   };//End WritingXML Class
}//End Namespace

int _tmain(void)
{
   using namespace PerfectXMLCPPSamplesBasics;
   WritingXMLUsingDOM* XMLHelper = new WritingXMLUsingDOM();

   XMLHelper->BuildXML();

   return 0;
}
Summary
Once you get some familiarity with the classes in the System.Xml namespace, working with XML in the .NET Framework becomes a very easy task to do. The goal of this three part article series is to help you learn using XML in the .NET Framework. This first part focused on creating XML documents, using the XmlTextWriter class (fast, forward-only, non-cached) and by using the XmlDocument (the W3C DOM implementation class). Part II will discuss some of the methods to parse, search and transform XML documents, and finally the Part III will present XML validation and serialization features in .NET. Part II and III will also contain ASP.NET examples in addition to the VB .NET, C#, and managed C++ console application sample applications as presented in this part. Stay tuned!

  Contact Us | E-mail Us | Site Guide | About PerfectXML | Advertise ©2004 perfectxml.com. All rights reserved. | Privacy