Click here to Skip to main content
Click here to Skip to main content

XML Directory Tree Generator

By , 17 Sep 2003
 

Introduction

This code snippet recursively enumerates all the folders and files under a given starting folder and generates an XML document as an output, that represents the same hierarchy as of the traversed directories. The resulting XML document can then be transformed with a simple XSLT transformation to generate the desired output. The main reason for writing this was to display the contents of a given directory on a website, without having to enable directory browsing on the server. I wanted to be able to define certain filter criteria to control the output generated, so it was important to capture as much information pertaining to the folders and files as possible.

Using the code

The code is implemented as FileSystemInfoLister class. The main entry point is the GetFileSystemInfoList() method that returns an XML document. The AddElements() method is called recursively until the entire path is traversed.

using System;
using System.Xml;
using System.IO;


namespace Hosca.FileSystemInfoLister {

    public class FileSystemInfoLister {

        XmlDocument xmlDoc;

        public FileSystemInfoLister() {
            xmlDoc = new XmlDocument();
        }

        public XmlDocument GetFileSystemInfoList(string StartFolder) {

            try{
                XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", 
                                                                "", "yes");
                xmlDoc.PrependChild ( xmlDec );
                XmlElement nodeElem =  XmlElement("folder",
                         new DirectoryInfo(StartFolder).Name); 
                xmlDoc.AppendChild(AddElements(nodeElem,StartFolder));
            }
            catch(Exception ex) {
                xmlDoc.AppendChild(XmlElement("error",ex.Message));
                return xmlDoc;
            }
            return xmlDoc;
        }


        private XmlElement AddElements(XmlElement startNode, 
                                               string Folder){
            try{
                DirectoryInfo dir = new DirectoryInfo(Folder);
                DirectoryInfo[] subDirs = dir.GetDirectories() ;
                FileInfo[] files = dir.GetFiles();
                foreach(FileInfo fi in files){
                  XmlElement fileElem = XmlElement("file",fi.Name);
                  fileElem.Attributes.Append(XmlAttribute("Extension", 
                                                           fi.Extension));
                  fileElem.Attributes.Append(XmlAttribute("Hidden", 
                   ((fi.Attributes & FileAttributes.Hidden) != 0) 
                                                       ? "Y":"N"));
                  fileElem.Attributes.Append(XmlAttribute("Archive", 
                   ((fi.Attributes & FileAttributes.Archive ) != 0) 
                                                       ? "Y":"N"));
                  fileElem.Attributes.Append(XmlAttribute("System", 
                   ((fi.Attributes & FileAttributes.System ) != 0) 
                                                       ? "Y":"N"));
                  fileElem.Attributes.Append(XmlAttribute("ReadOnly", 
                   ((fi.Attributes & FileAttributes.ReadOnly ) != 0) 
                                                        ? "Y":"N"));
                  startNode.AppendChild(fileElem);
                }
                foreach (DirectoryInfo sd in subDirs) {
                  XmlElement folderElem = XmlElement("folder",sd.Name);
                  folderElem.Attributes.Append(XmlAttribute("Hidden", 
                    ((sd.Attributes & FileAttributes.Hidden) != 0) 
                                                       ? "Y":"N"));
                  folderElem.Attributes.Append(XmlAttribute("System", 
                    ((sd.Attributes & FileAttributes.System  ) != 0) 
                                                       ? "Y":"N"));
                  folderElem.Attributes.Append(XmlAttribute("ReadOnly",
                    ((sd.Attributes & FileAttributes.ReadOnly ) != 0)
                                                       ? "Y":"N"));
                  startNode.AppendChild(AddElements(folderElem,
                                                     sd.FullName));
                }
                return startNode;
            }
            catch(Exception ex) {
                return XmlElement("error",ex.Message);
            }
        }
        private XmlAttribute XmlAttribute(string attributeName, 
                                          string attributeValue){
            XmlAttribute xmlAttrib = 
                xmlDoc.CreateAttribute(attributeName);
            xmlAttrib.Value = FilterXMLString(attributeValue);
            return xmlAttrib;
        }
        private XmlElement XmlElement(string elementName, 
                                       string elementValue){
            XmlElement xmlElement = xmlDoc.CreateElement(elementName);
            xmlElement.Attributes.Append(XmlAttribute("name", 
                                      FilterXMLString(elementValue)));
            return xmlElement;
        }
        private string FilterXMLString(string inputString){
            string returnString = inputString;
            if (inputString.IndexOf("&") > 0){
                returnString = inputString.Replace("&","&");
            }
            if (inputString.IndexOf("'") > 0){
                returnString = inputString.Replace("'","'");
            }
            return returnString;
        }    
    }
}

Points of interest

The only minor point of interest is that there are 2 characters that are allowable in file names but not in XML documents. In fact there are 5 characters that are not allowed in XML documents. They are :

< < less than
> > greater than
& & ampersand
&apos; ' apostrophe
" " quotation mark

Fortunately for us, Windows takes care of 3 of them. However the ampersand and the apostrophe which are legal to use in file names will have to be handled manually. The FilterXMLString method takes care of the details.

History

  • Sept 18 2003 - Initial version (1.0.0)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Erhan Hosca
Web Developer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1membermovhlps29 Nov '08 - 5:20 
.
QuestionEditing XML filememberKrishnakumar Bhagwat21 Aug '06 - 21:17 
HI Ppl,
 
I have an XML file as below..
XmlDocument xmlDoc = new XmlDocument();
XmlNode xn = xmlDoc.CreateElement("Recent");
xmlDoc.AppendChild(xn);
XmlDeclaration xd = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
XmlNode root = xmlDoc.SelectSingleNode("/Recent");
XmlElement mainNode = xmlDoc.CreateElement("Query");
XmlAttribute Name_attr = xmlDoc.CreateAttribute("Name");
XmlAttribute Scr_attr = xmlDoc.CreateAttribute("Script");
 
i = 1;
Name_attr.InnerText = recent_query+(i);
i = i + 1;
Scr_attr.InnerText = cmdText;
 
mainNode.Attributes.Append(Name_attr);
mainNode.Attributes.Append(Scr_attr);
root.AppendChild(mainNode);
xmlDoc.Save("C:\\xml\\recent.xml");
I am using ASP.NET...
Problem I am facing is that.. whenevr I run this program, old value is replaced by the new value I am giving.. but I would like to retain the old value present in that and add the new value to it and store the same. Please let me know how do I do that...
My text value which is to be stored will be present in "cmdText".
 
Krishna

AnswerappmemberMember 45204226 Dec '09 - 1:27 
Can I pass parameters to the demo project application? Like which folder and where XML to save?
GeneralSmall bug in FilterXMLStringmemberRichard Poole12 May '04 - 6:15 
Apart from the ampersand replacement issue which has already been mentioned I've noticed that the apostrophe replacement code in FilterXMLString is using inputString as its source instead of returnString. If a filename contains both ampersands and apostrophes, the if (inputString.IndexOf("'") > 0) { ... } block will overwrite the modified returnString that was generated by the if (inputString.IndexOf("&") > 0) block.
 
This should fix it (incidentally, aren't the IndexOf calls unnecessary?): -
 
private string FilterXMLString(string xmlString)
{
    xmlString = xmlString.Replace("&", "&");
    xmlString = xmlString.Replace("'", "&apos;");
    return xmlString;
}
 
Hope this helps,
Richard.
GeneralRe: Small bug in FilterXMLStringmemberErhan Hosca12 May '04 - 7:15 
nice catch...
 
if you're going to brute-force anyway without bothering to check to see if you really need to, then it could be simplified further.
 
private string FilterXMLString(string xmlString)
{
    return xmlString.Replace("&", "&_amp").Replace("'", "&_apos");
}

GeneralRe: Small bug in FilterXMLStringmemberdjarn29 Oct '04 - 22:34 
the FilterXMLString method is not really needed as the Value setter in XmlAttribute seems to do the job itself.
GeneralHandling AmpersandsmemberDonald Xie11 Jan '04 - 15:11 
Good code and explanations. However, there is perhaps an HTML formatting issue - in the FilterXMLString() function, you have:
    if (inputString.IndexOf("&") > 0){
        returnString = inputString.Replace("&","&");
    }
I guess you really mean to replace the symbol "&" with the encoded HTML string "& a m p ;" (I also have to embed a space between charaters to prevent the message board from converting it automatically).
 
Cheers,
 
Donald Xie
-----------------------------
“A complex system that works is invariably found to have evolved from a simple system that worked… A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.” — John Gall in Systemantics: How Systems Really Work and How They Fail
GeneralRe: Handling AmpersandsmemberErhan Hosca12 Jan '04 - 3:29 
that is exactly the case: the &_ersand got rendered as "&"..
 
please use the sample project file to get the correct version
GeneralVery cool - I want more!sussShem Stimpy25 Sep '03 - 3:32 
This works very well. Nice interface and nice design. It would be very cool if it could grab file properties. It could be the foundation for a document management system. For example, jpgs with keywords to allow someone to sort, display, or filter home photos.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 18 Sep 2003
Article Copyright 2003 by Erhan Hosca
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid