tsJensen

A quest for software excellence...

Value of Schema Driven XML Configuration

If you've written an ASP.NET application, you've already taken advantage of schema driven XML configuration perhaps without even realizing it. You may have even ignored the web.config file if you're just starting out with ASP.NET. Or you may have used it extensively without considering the usefulness of using such a construct in your own applications.

My day job involves writing a complex application that implements a myriad of business rules, nearly all of which non-coders need to be able to modify from time to time without bothering the development team. That means using XML. But not just any old XML. I'm talking about well formed, validated XML using a nice XSD schema file controlled by the development team.

I recommend the reader purchase O'Reilly's book XML Schema by Eric van der Vlist. It's been a most trustworthy companion. And then to make the use of this XML configuration dreamscape even easier, I recommend Christian Weyer's contract first tool. I use this extremely useful tool to generate the XAL (XML access layer) so I don't have to write it by hand.

With these two tools in hand, you can create something like this in Visual Studio:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="watchconfig" targetNamespace="http://example.com/v1001/watchconfig.xsd"
    elementFormDefault="qualified"
    xmlns="http://example.com/v1001/watchconfig.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="watchconfig">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="watchgroup" minOccurs="0" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="watchgroup">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="watcher" minOccurs="0" maxOccurs="unbounded" />
            </xs:sequence>
            <xs:attribute name="id" type="xs:string" use="required" />
            <xs:attribute name="literalsconfigpath" type="xs:string" />
        </xs:complexType>
    </xs:element>
    <xs:element name="watcher">
        <xs:complexType>
            <xs:attribute name="sourcepath" type="xs:string" use="required" />
            <xs:attribute name="wippath" type="xs:string" use="required" />
            <xs:attribute name="outpath" type="xs:string" use="required" />
            <xs:attribute name="ssispath" type="xs:string" use="required" />
            <xs:attribute name="intakemap" type="xs:string" use="required" />
        </xs:complexType>
    </xs:element>
</xs:schema>

And an XML file like this:

<?xml version="1.0" encoding="utf-8" ?>
<watchconfig xmlns="http://example.com/v1001/watchconfig.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <watchgroup id="clientname" literalsconfigpath="H:\temp\literals_client">
        <watcher sourcepath="H:\temp\client\watch"
                    wippath="H:\temp\client\wip"
                    outpath="H:\temp\client\out"
                    ssispath="H:\temp\client\ssis"
                    intakemap="D:\Contracts\Code\map.xml" />
    </watchgroup>
</watchconfig>

And the nicely generated code like this:

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, TypeName="watchconfig")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.com/v1001/watchconfig.xsd", IsNullable=false, ElementName="watchconfig")]
[System.ComponentModel.TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public partial class Watchconfig
{

/// <remarks/>
private System.Collections.Generic.List<Watchgroup> watchgroup;

public Watchconfig()
{
}

public Watchconfig(System.Collections.Generic.List<Watchgroup> watchgroup)
{
this.watchgroup = watchgroup;
}

[System.Xml.Serialization.XmlElementAttribute("watchgroup", Order=0)]
public System.Collections.Generic.List<Watchgroup> Watchgroup
{
get
{
return this.watchgroup;
}
set
{
if ((this.watchgroup != value))
{
this.watchgroup = value;
}
}
}
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, TypeName="watchgroup")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.com/v1001/watchconfig.xsd", IsNullable=false, ElementName="watchgroup")]
[System.ComponentModel.TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public partial class Watchgroup
{

/// <remarks/>
private System.Collections.Generic.List<Watcher> watcher;

/// <remarks/>
private string id;

/// <remarks/>
private string literalsconfigpath;

public Watchgroup()
{
}

public Watchgroup(System.Collections.Generic.List<Watcher> watcher, string id, string literalsconfigpath)
{
this.watcher = watcher;
this.id = id;
this.literalsconfigpath = literalsconfigpath;
}

[System.Xml.Serialization.XmlElementAttribute("watcher", Order=0)]
public System.Collections.Generic.List<Watcher> Watcher
{
get
{
return this.watcher;
}
set
{
if ((this.watcher != value))
{
this.watcher = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="id")]
public string Id
{
get
{
return this.id;
}
set
{
if ((this.id != value))
{
this.id = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="literalsconfigpath")]
public string Literalsconfigpath
{
get
{
return this.literalsconfigpath;
}
set
{
if ((this.literalsconfigpath != value))
{
this.literalsconfigpath = value;
}
}
}
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, TypeName="watcher")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.com/v1001/watchconfig.xsd", IsNullable=false, ElementName="watcher")]
[System.ComponentModel.TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public partial class Watcher
{

/// <remarks/>
private string sourcepath;

/// <remarks/>
private string wippath;

/// <remarks/>
private string outpath;

/// <remarks/>
private string ssispath;

/// <remarks/>
private string intakemap;

public Watcher()
{
}

public Watcher(string sourcepath, string wippath, string outpath, string ssispath, string intakemap)
{
this.sourcepath = sourcepath;
this.wippath = wippath;
this.outpath = outpath;
this.ssispath = ssispath;
this.intakemap = intakemap;
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="sourcepath")]
public string Sourcepath
{
get
{
return this.sourcepath;
}
set
{
if ((this.sourcepath != value))
{
this.sourcepath = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="wippath")]
public string Wippath
{
get
{
return this.wippath;
}
set
{
if ((this.wippath != value))
{
this.wippath = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="outpath")]
public string Outpath
{
get
{
return this.outpath;
}
set
{
if ((this.outpath != value))
{
this.outpath = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="ssispath")]
public string Ssispath
{
get
{
return this.ssispath;
}
set
{
if ((this.ssispath != value))
{
this.ssispath = value;
}
}
}

[System.Xml.Serialization.XmlAttributeAttribute(AttributeName="intakemap")]
public string Intakemap
{
get
{
return this.intakemap;
}
set
{
if ((this.intakemap != value))
{
this.intakemap = value;
}
}
}
}

(Sorry, indentation got hosed, but you won't need it since you'll be generating this code with one click.)