This is an update to a project I wrote using the .NET alpha release. This version
works with the .NET Beta 2 release.
I was looking for a useful little project to start learning C# and the .NET framework.
In my job we do a lot of things with XML so I was interested in exercising some of
the XML classes. In particular I wanted to see how XML and ADO+ fit together. The
result is the code in this project.
This code generates a list of entries in a directory as XML. The XML can be
returned to the caller as an XML string, a W3C DOM document, or an ADO+ DataSet
.
A word of WARNING. To compile the attached code, you need to have the .NET Framework SDK
Beta 2 installed which. You can get the .NET Framework SDK here.
Please don't send me questions regarding installation of the SDK.
Below is the source code for the XML directory lister:
using System;
using System.IO;
using System.Xml;
using System.Data;
namespace GregHack.XMLStuff.Cs
{
public class XMLDirectoryLister
{
private Boolean m_bUseDOMCalls;
public XMLDirectoryLister( Boolean bUseDOMCalls )
{
m_bUseDOMCalls = bUseDOMCalls;
}
public string
getXMLString( string strDirectory )
{
string strXML = "";
XmlDocument doc = getXML( strDirectory );
if ( doc != null )
{
StringWriter writer = new StringWriter();
doc.Save( writer );
strXML = writer.ToString();
}
doc = null;
return strXML;
}
public DataSet
getXMLDataset( string strDirectory )
{
DataSet ds = null;
string strXML = getXMLString( strDirectory );
XmlDataDocument datadoc = new XmlDataDocument();
datadoc.DataSet.ReadXml( new StringReader(strXML) );
ds = datadoc.DataSet;
return ds;
}
public XmlDocument
getXMLDocument( string strDirectory )
{
return getXML( strDirectory );
}
private XmlDocument
getXML( string strDirectory )
{
XmlDocument xmlDoc;
if ( m_bUseDOMCalls )
xmlDoc = getXMLUsingDOMCalls( strDirectory );
else
xmlDoc = getXMLUsingTextWriterClass( strDirectory );
return xmlDoc;
}
private XmlDocument
getXMLUsingDOMCalls( string strDirectory )
{
XmlDocument doc = new XmlDocument();
XmlDeclaration dec =
doc.CreateXmlDeclaration("1.0", "", "yes");
doc.PrependChild ( dec );
XmlElement nodeElem =
doc.CreateElement( "dirlist" );
doc.AppendChild( nodeElem );
Boolean bFirst = true;
DirectoryInfo dir = new DirectoryInfo( strDirectory );
foreach ( FileSystemInfo entry in dir.GetFileSystemInfos() )
{
if ( bFirst == true )
{
XmlElement root = doc.DocumentElement;
String strFullName = entry.FullName;
String strFileName = entry.Name;
String strDir = strFullName.Substring( 0, strFullName.Length - strFileName.Length );
root.SetAttribute ("dir", strDir );
bFirst = false;
}
XmlElement elem = doc.CreateElement("entry");
doc.DocumentElement.AppendChild(elem);
addTextElement( doc, elem, "name", entry.Name );
addTextElement( doc, elem, "created", entry.CreationTime.ToString() );
addTextElement( doc, elem, "lastaccess", entry.LastAccessTime.ToString() );
addTextElement( doc, elem, "lastwrite", entry.LastWriteTime.ToString() );
addTextElement( doc, elem, "isfile", ( (entry.Attributes & FileAttributes.Directory) > 0 ) ? "False" : "True" );
addTextElement( doc, elem, "isdir", ( (entry.Attributes & FileAttributes.Directory) > 0 ) ? "True" : "False" );
addTextElement( doc, elem, "readonly", ( (entry.Attributes & FileAttributes.ReadOnly) > 0 ) ? "True" : "False" );
}
return doc;
}
private void
addTextElement( XmlDocument doc, XmlElement nodeParent, string strTag, string strValue )
{
XmlElement nodeElem = doc.CreateElement( strTag );
XmlText nodeText = doc.CreateTextNode( strValue );
nodeParent.AppendChild( nodeElem );
nodeElem.AppendChild( nodeText );
}
private XmlDocument
getXMLUsingTextWriterClass( string strDirectory )
{
StringWriter writerString = new StringWriter();
XmlTextWriter writer = new XmlTextWriter( writerString );
writer.WriteStartDocument(true);
Boolean bFirst = true;
DirectoryInfo dir = new DirectoryInfo( strDirectory );
foreach ( FileSystemInfo entry in dir.GetFileSystemInfos() )
{
if ( bFirst == true )
{
writer.WriteStartElement( "dirlist", "" );
String strFullName = entry.FullName;
String strFileName = entry.Name;
String strDir = strFullName.Substring( 0, strFullName.Length - strFileName.Length );
writer.WriteAttributeString( "dir", strDir );
bFirst = false;
}
writer.WriteStartElement( "entry", "" );
writer.WriteElementString( "name", entry.Name );
writer.WriteElementString( "created", entry.CreationTime.ToString() );
writer.WriteElementString( "lastaccess", entry.LastAccessTime.ToString() );
writer.WriteElementString( "lastwrite", entry.LastWriteTime.ToString() );
writer.WriteElementString( "isfile", ( (entry.Attributes & FileAttributes.Directory) > 0 ) ? "False" : "True" );
writer.WriteElementString( "isdir", ( (entry.Attributes & FileAttributes.Directory) > 0 ) ? "True" : "False" );
writer.WriteElementString( "readonly", ( (entry.Attributes & FileAttributes.ReadOnly) > 0 ) ? "True" : "False" );
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
string strXML = writerString.ToString();
StringReader reader = new StringReader( strXML );
XmlDocument doc = new XmlDocument();
doc.Load( reader );
return doc;
}
}
}
The first thing of interest is the following code:
namespace GregHack.XMLStuff.Cs
This says that I am creating a namespace for my code. Code that references the
XMLDirectoryLister
will have to include the following line:
using GregHack.XMLStuff.Cs;
The meat of the work is done in the getXMLUsingDOMCalls()
and
the getXMLUsingTextWriterClass()
methods. They both create
a W3C document via the XmlDocument
class of the .NET framework but using
alternate methods to do so. The first method uses the XmlDocument
class
of the framework to add to and traverse the XML document. The second method uses the
XMLTextWriter
class to accomplish the same thing.
Both implementations loop through all of the entries in the directory using the
foreach
syntax of C#:
foreach ( FileSystemEntry entry in dir.GetFileSystemEntries() )
The XmlDocument
version uses the AppendChild()
method
to insert elements into the document that represent the entries in the directory. The
XMLTextWriter
version uses WriteElementString()
to accomplish
the same thing.
Something that I thought was pretty cool is how easy it is to translate XML into
an ADO+ DataSet
. The following lines of code that are in the getXMLDataset
are all that are needed:
DataSet ds = null;
string strXML = getXMLString( strDirectory );
XmlDataDocument datadoc = new XmlDataDocument();
datadoc.DataSet.ReadXml( new StringReader(strXML) );
ds = datadoc.DataSet;
return ds;
The ReadXml
method will parse the XML passed in and come up
with a database schema based on the XML.
The sample project contains a command line test .exe and a the XMLDirectoryLister
compiled
as a DLL. It generates XML of the form:
<?xml version='1.0' ?>
<dirlist dir="C:\anet\XML Directory\">
<entry>
<name>XMLDirectoryLister.bak</name>
<created>9/21/2000 21:13:34</created>
<lastaccess>9/21/2000 00:00:00</lastaccess>
<lastwrite>9/21/2000 21:53:54</lastwrite>
<isfile>True</isfile>
<isdir>False</isdir>
<readonly>False</readonly>
</entry>
<entry>
<name>XMLDirectoryLister.dll</name>
<created>9/21/2000 19:19:13</created>
<lastaccess>9/21/2000 00:00:00</lastaccess>
<lastwrite>9/21/2000 21:12:20</lastwrite>
<isfile>True</isfile>
<isdir>False</isdir>
<readonly>False</readonly>
</entry>
</dirlist>
When the getXMLDataset()
method is called, it returns a DataSet
with two tables dirlist
and entry
. Entry has 7 columns,
name, created, lastaccess, lastwrite, isfile, isdir, readonly
. You can see the
relationship between the XML and the tables.
One last thing of interest is the class and method documentation. You'll notice it
uses three forward slashes and some XML tags. If
you use this style you can tell the compiler to emit XML documentation as output. It
will then create and XML file containing the documentation you put in your source file.
The XML could then be rendered as HTML or some other format to document you source. Use
a specifier in your makefile such as /doc:XMLDirectoryLister.xml
to emit
the XML documentation file. The sample project contains the XML documentation emitted
by the compiler.