Creating an XML File with VB 2010

Introduction

"If you want to improve, be content to be thought foolish and stupid."
--Epictetus

I have been in the computer business long enough to remember removing physical disk platters--Winchester drive--from mini computers during data processing. I can remember pre-hard disks for PCs when a missing floppy caused a hard system crash and a solution was to hook the hard error interrupt 0x24 to contrive a sort of global exception handler. It seems like in those days one had to write every stitch of code by hand because the notion of frameworks was pretty foreign; we only had API's and everything had to be handled at a pretty low level. It wasn't uncommon to have assembly language modules as part of your build process.

Today a lot of the skills mentioned are probably only worth perspective. If you are a .NET developer then the .NET framework and to a lesser extent the Windows API is where you should be spending your time. Granted small things are a lot easier to do with a great framework. The only problem is there are more things to do in a given application than ever before.

For this article I elected to cover converting common data to an XML file and inferring the supporting XML Schema from that XML file. The solution relies heavily on LINQ to XML and functional composition. Functional composition means to use functions to create something. By working through this example you should learn some things about the structure of XML documents, XML schemas, and File IO.

Composing an XML Document from the File System

A clever application I used for a while was a wrapper for Visual Source Safe that let a user check in and check out files from a Web application. On the back end the Web application used SourceSafe's Automation layer to read SourceSafe's contents. The contents were converted into a file-like structure on a Web page and end user operations were sent back to the server, again using the Automation layer of SourceSafe to actually perform the operations and then deliver the content across the Web. The demo sort of simulates that; instead of SourceSafe the file system was used. The first part of the sample uses the Directory class in System.IO to enumerate all file system entries. The second part of the sample uses the file paths to create FileInfo objects and store these in a generic list. Taken from Listing 1 that code looks like this.

Dim files = Directory.EnumerateFileSystemEntries("C:\Books\Earthweb", "*.*",
  SearchOption.AllDirectories)

Dim infos As List(Of FileInfo) = New List(Of FileInfo)
infos.Clear()
Array.ForEach(files.ToArray(), Sub(f)
  Dim info As FileInfo = New FileInfo(f)
  If (info Is Nothing = False) Then infos.Add(info)
End Sub)

The first statement returns all files in all directories beginning with the directory defined in the first argument. Programmers used to have to write the recursive descent through the file system but not anymore. The second fragment uses Array.ForEach and a multline Lambda Sub-expression to store the FileInfo objects.

A Lambda expression is a condensed anonymous method. Basically, a Lambda expression is just a method with the extra syntactical elements removed-like qualifiers, method names, and argument types. Lambda expressions are commonly used where methods accept arguments of a generic delegate type like Func, Action, and Predicate. Here is Listing 1; it contains the directory reading statements and the LINQ to XML code.

Imports System.IO
Imports System.Xml.Linq
Imports System.Threading.Tasks
Imports System.Windows.Forms
Imports System.Xml.Schema
Imports System.Text
Imports System.Xml


Module Module1

    Sub Main()

      Dim files = Directory.EnumerateFileSystemEntries("C:\Books\Earthweb", "*.*",
        SearchOption.AllDirectories)

      Console.WriteLine("=================================")
      Console.WriteLine("Found: {0}", files.Count)
      Console.WriteLine("=================================")

      Dim infos As List(Of FileInfo) = New List(Of FileInfo)
      infos.Clear()
      Array.ForEach(files.ToArray(), Sub(f)
        Dim info As FileInfo = New FileInfo(f)
        If (info Is Nothing = False) Then infos.Add(info)
      End Sub)

      Dim doc As XDocument = New XDocument(
        New XDeclaration("1.0", "utf-8", "true"),
        New XElement("CodeGuru",
          From info In infos
          Let ro = IIf(info.IsReadOnly, "+R", "-R")
          Select New XElement("File",
            New XAttribute("Attributes", info.Attributes),
            New XElement("FileName", info),
            New XElement("CreationTime", info.CreationTime),
            New XElement("ReadOnly", ro)
            )))

      ' save xml document with today's date
      doc.Save(String.Format("..\..\{0}.xml",
        DateTime.Now.ToShortDateString().Replace("/", "_")))

      Console.ReadLine()


End Sub

Listing 1: Using File IO to read the file system and LINQ to XML to convert that data to an XML document.

Remember Functional Composition means to compose with function calls. The XDocument class is part of the System.Xml.Linq namespace. It represents an XML document. Write code like New XDocument(... is an example of Functional composition.

XDocument accepts a ParamArray of subordinate XML elements like XElement and XAttribute. The nested LINQ to XML statement begins with the From info in infos part of the compositor. The range variable--like an iterator variable in a for loop-is info. The source collection follows the in-clause. Lets define a temporary range variable that can be used for loops or in this case to add logic to the LINQ query. When you see "Select New" think projection. A projection can yield a new anonymous type or be used to construct an existing type as in this example.

The root element in the final document will be CodeGuru. The first child nodes will be File with the file's attributes and the nested children FileName, CreationTime, and

ReadOnly

. An excerpt from the output of the code can be seen in Listing 2.

<?xml version="1.0" encoding="utf-8"?>
<CodeGuru>
  <File Attributes="Directory">
    <FileName>C:\Books\Earthweb\GENERAL ARTICLES</FileName>
    <CreationTime>2009-04-19T16:10:22.355457-04:00</CreationTime>
    <ReadOnly>-R</ReadOnly>
  </File>
  <File Attributes="Archive">
    <FileName>C:\Books\Earthweb\Schedule_for_VB_Today.doc</FileName>
    <CreationTime>2009-04-19T16:10:22.355457-04:00</CreationTime>
    <ReadOnly>-R</ReadOnly>
  </File>
  <File Attributes="Directory">
    <FileName>C:\Books\Earthweb\SOURCE</FileName>
    <CreationTime>2009-04-19T16:10:22.6518589-04:00</CreationTime>
    <ReadOnly>-R</ReadOnly>
  </File>
  <File Attributes="Directory">
    <FileName>C:\Books\Earthweb\Videos</FileName>
    <CreationTime>2009-04-19T16:10:30.2959079-04:00</CreationTime>
    <ReadOnly>-R</ReadOnly>
  </File>
…

Listing 2: Part of the output from Listing 1 are some of the file contents from 11 years of writing the VB Today column. (There were 4000 elements on this particular PC.)



Creating an XML File with VB 2010

Inferring the XML Schema from the XML Document

Sometimes you want to know the schema of an XML document too. XSD (or XML schema documents) describe the contents of a particular XML document and schema documents are XML documents too. By adding the following two lines to the Main sub-routine in Listing 1 and the CreateXSD method in Listing 3 you can infer the schema for any XML document, again using the .NET framework to do the heavy lifting for you.

' infer XML schema
Dim schema As XDocument = CreateXSD(doc)
schema.Save(String.Format("..\..\{0}.xsd", 
  DateTime.Now.ToShortDateString().Replace("/", "_")))

' Returns XML Schema Document
Public Function CreateXSD(ByVal doc As XDocument) As XDocument

  Dim inference As XmlSchemaInference = New XmlSchemaInference

  Using stream As MemoryStream = 
    New MemoryStream(Encoding.ASCII.GetBytes(doc.ToString()))
    Dim reader As XmlTextReader = New XmlTextReader(stream)
    Dim schemaSet As XmlSchemaSet = inference.InferSchema(reader)
    Dim schema As XmlSchema = schemaSet.Schemas()(0)
    Using target As TextWriter = New StringWriter()
      schema.Write(target)
      Return XDocument.Parse(target.ToString())
    End Using
  End Using
End Function

Listing 3: the CreateXSD method.

Most of the methods in Listing 3 come from the System.Xml and System.Xml.Schema namespaces. The XmlSchemaInference class does the heavy lifting of examining the XML content and deriving the schema.

The stream classes like MemoryStream are part of System.IO. Think of stream objects as buffers for moving data round. In CreateXSD the content of the data XML document are read into memory then into an XmlTextReader. From there the reader is used to infer and create the schema set, and a TextWriter is used to store the schema text which in turn is moved to a new XDocument.

CreateXSD was borrowed from an earlier article here. You may be able to accomplish schema inference with fewer streams, but working code saves time for writers too. The XSD document based on the code is provided in listing 4.

<?xml version="1.0" encoding="utf-16"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="CodeGuru">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="File">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="FileName" type="xs:string" />
              <xs:element name="CreationTime" type="xs:dateTime" />
              <xs:element name="ReadOnly" type="xs:string" />
            </xs:sequence>
            <xs:attribute name="Attributes" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Listing 4: The XML Schema produced the code from the sample and the resultant XML document.

Summary

LINQ to XML is an implementation of LINQ for XML. It competes with the XPath technology. The reason I use it instead of XPath is that LINQ grammar can be shared across technologies like SQL and objects, but XPath is for XML only. You could accomplish the same thing-convert data to XML-using other approaches, but LINQ to XML excels at this task.

Functional composition is literally constructing something with function calls. It is the same concept that make technologies like framework reflection and emitting and the CodeDOM (code generating namespaces) work.

Related Articles





About the Author

Paul Kimmel

Paul Kimmel is the VB Today columnist for CodeGuru and has written several books on object-oriented programming and .NET. Check out his upcoming book Professional DevExpress ASP.NET Controls (from Wiley) now available on Amazon.com and fine bookstores everywhere. Look for his upcoming book Teach Yourself the ADO.NET Entity Framework in 24 Hours (from Sams). You may contact him for technology questions at pkimmel@softconcepts .com. Paul Kimmel is a Technical Evangelist for Developer Express, Inc, and you can ask him about Developer Express at paulk@devexpress.com and read his DX blog at http:// community.devexpress.com/blogs/paulk.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Not long ago, security was viewed as one of the biggest obstacles to widespread adoption of cloud-based deployments for enterprise software solutions. However, the combination of advancing technology and an increasing variety of threats that companies must guard against is rapidly turning the tide. Cloud vendors typically offer a much higher level of data center and virtual system security than most organizations can or will build out on their own. Read this white paper to learn the five ways that cloud …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds