Introduction
I started writing this application some time ago when I was involved in a project with a massive use of XML. What this application does is basically replicate the features of the XSD tool by Microsoft, but with some additional nice features.
Background
The core functionalities of the application are written leveraging the features of XML and the CodeDom of the .NET framework. There are some options that can be quite useful, like automatic conversion from XML attributes to field / property couples. Also, you can tell the parser to use the generic namespace to create a collection of children instead of arrays. If you want to add some feature, I would suggest some knowledge of the Sytem.CodeDom
namespace.
Using the code
I need the code file only
If you just want to use a tool that may help your daily work, the application itself is so simple that you don't need too many instructions... just grab an XML schema, provide an output path (by default, it is in the same location of the schema), and a filename. Once you've done this, you can select some nice options like the target language, the use of generics instead of standard arrays, a default namespace, and some others. With these settings, you are a couple of clicks away from obtaining your fresh class file! Just click on Compile and Save, and you're done. Note: All the classes are generated as partial
, to allow extensibility without bothering of regeneration of classes (in case your schema is growing).
Today, I think I'll add something...
If instead you want to create your own UI, or want to use the parsing features in other projects, all of them are contained in a single static class called codeProcessor
. The usage of this class is extremely easy. Indeed, if you want to create a class file from a schema, you have to call just one method:
public static CodeNamespace Process(string xsdFile,
string targetNamespace,
bool chooseElement,
string elementName)
All the "intensive tasks" are inside this single method. In particular, the method is divided in three regions, one for the XML schema parsing, one for the CodeDom generation, and one for the CodeDom authoring.
XML schema parsing should not have to be modified, it just compiles the schema in memory. The "extra features" like adding comments and default values are handled in CodeDom generation. If you want to manage other features, my suggestion is to use this section to handle XML fragments, and the CodeDom authoring section to create the real code elements. For example, the attribute default values are managed like this:
var defaultAtts = from XmlSchemaAttribute att in
((XmlSchemaComplexType)element).Attributes.Cast<xmlschemaattribute>()
where att.DefaultValue != null
select att;
if(defaultAtts.Count()>0){
foreach (XmlSchemaAttribute attribute in defaultAtts)
{
member.UserData.Add(string.Format("default.{0}", attribute.Name),
attribute.DefaultValue);
}
}
where member
is an internal variable used to manage the CodeMember
object compiled from the schema. In this way, during the CodeDom authoring operations, the UserData collection is parsed by creating the right code snippet.
CodeDom Authoring takes care of creating the code snippets in the form of CodeDom objects. If you have never faced this part of the .NET Framework, my suggestion is to read some nice article on that and try to delve inside. In this section are handled all the operations related to the "extra features" like changing from arrays to generics, including namespaces, default values, and so on. It also adds a default constructor (without parameters) to perform initialization tasks. For example:
foreach(CodeTypeDeclaration defaultV in defaultValueTypes){
foreach(System.Collections.DictionaryEntry kvp in defaultV.UserData){
ctor.Statements.Add(new CodeAssignStatement(
new CodeVariableReferenceExpression(kvp.Key.ToString().Substring(8)),
new CodeSnippetExpression(kvp.Value.ToString())));
}
}
In the snippet above is reported the management of default values for code generation. By checking all the types that have default values, statements are added in the default constructor to initialize the value of each field. If you are a newbie to the CodeDom namespace, it could be a bit tricky to understand the snippet. Here is the explanation of how it works: The ctor
variable, of type CodeTypeConstructor
, has a collection of statements, in which are added all the default assignments. This is achieved by creating a CodeAssignStatement
, where a left and right expression have to be specified. The left expression is the reference to the field's name, and the right expression is a snippet (that allows you to put normal text inside a CodeDom object with the default value specified in the XML schema). The result of the snippets above should look like this:
This is the XML schema snippet:
...
<xs:complexType name="ControlProperties" abstract="true">
<xs:attribute name="Enabled" type="xs:boolean"
use="optional" default="true">
...
And this is the code snippet generated:
[System.Xml.Serialization.XmlIncludeAttribute(typeof(GridViewProperties))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("XMLtoClass", "1.0.0.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schemaName.xsd")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://schemaName.xsd",
IsNullable=true)]
public abstract partial class ControlProperties {
private bool enabledField;
public ControlProperties() {
this.enabledField = true;
}
Points of interest
I think that the biggest POIs are the "extra" functions provided by the application, allowing to put comment blocks and default values, things that are just a dream with the standard XSD tool!
What's next
What I would like to add in a later version is the option to choose between a single monolithic file or split all the classes into single files, maybe stored in a folder structure. This could help improve some post-creation work which are really annoying.
And of course, if anybody wants to contribute, you are always more than welcome!