Abstract
This article describes how we can use XML as a metadata source for SCG Templates. It also shows step-by-step instructions of how to generate strongly typed objects from XSD and use it in the templates to generate text based output. ASP.NET developers can also use this paper as reference to transform XML using ASP.NET scripts and avoid XSLT.
SmartCodeGenerator
SmartCodeGenerator (SCG) is an ASP.NET 2.0 website OR an ASP.NET 1.1 web application and is a fully-fledged template-based code generator that allows you to generate code for any text language. Templates are written in any ASP.NET IDE such as Visual Studio, as ASP.NET User Controls.
The current feature list of SmartCodeGenerator includes:
- SCG Project is an ASP.NET Web project that uses existing ASP.NET 2.0 or ASP.NET 1.1 web application concepts
- All development can be done in VS2005 or VS2003
- SCG Projects can be run on Cassini Webserver
- SCG Projects can be built and run using NAnt
- Use XML metadata in your template.
- Extensible Template Generation Engine
- Open source Database Schema discovery API for MSSQL, Oracle and MySQL
- Generates text based output
- Generates output in batch mode
- Fully customisable template-based code generation
- Remembers custom property data entries
- Intellisense, compilation, debug, code snippet, souce view, design view and all other countless features that are offered by the Visual Studio IDE
For the latest downloads please refer to
Codeplex. This article is based on CTP 2.7 and tries to answer how to use SmartCodeGenerator to generate output depending on XML metadata. For an architectural overview and usage overview of the SCG framework please refer to the following three articles at The Code Project,
"
SmartCodeGenerator - Code Generation experience with Visual Studio and ASP.NET- Architectural Overview".
"
SmartCodeGenerator - Code Generation experience with Visual Studio and ASP.NET- Usage Overview".
"SmartCodeGenerator -Code Generation experience with ASP.NET, NAnt and Cassini"
Introduction
We all agree that XML's primary purpose is to facilitate the sharing of data across different systems. However, the same XML can also be used as a powerful input for template generation tools. CodeSmith fans might be already familiar with XmlProperty and can be already seen in a variety of examples. In this article I am going to illustrate how to use XML as a metadata source for SCG templates. XML provides a text-based means to describe and apply a tree-based structure to information and SCG facilitates writing logic and code in your favorite .NET language (C#, Vb.Net etc). By combining these two technologies, we can generate meaningful input & output for a system which was very complex to achieve earlier. In the code generation world, XML is now highly used as a metadata source to generate business objects, data access layers and even user interfaces. The other practice is to generate different application layers based on database structure, but XML is becoming more popular as it provides more flexibility in integrating logic as a metadata source for templates. For example, in this example I defined the class name and its properties, and also specified to generate a UI with a gridview component and paging support. Notice this would have been a bit messy to define in a database structure.
<?xml version="1.0" encoding="utf-8" ?>
<BusinessObjects xmlns=""http://www.smartcodegenerator.com/"">http://www.smartcodegenerator.com">
<class name="Student">
<properties>
<property name="Id" type="String" maxSize="36" />
<property name="Name" type="String" maxSize="96" />
</properties>
<userinterface generate="true">
<uicomponent>GridView</uicomponent>
<gridview paging ="true" rownum="20">
</gridview>
</userinterface>
</class>
</BusinessObjects>
Lets see how to use XML in SCG template.
How to use XML metadata in SCG templates?
To explain this technique, I will use a simple example to generate HTML from XML and later on look at a more useful example where we will define business objects in XML format and then generate an object layer, a data access layer, a number of tables and Stored Procedures using SCG.
Step 1: Define your XML schema and generate XSD
Lets take this sample XML students.xml where student related data like id, name, courses etc. are stored.
<Students xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">http://www.w3.org/2001/XMLSchema">
<Student>
<ID>2</ID>
<FirstName>Chris</FirstName>
<LastName>Heale</LastName>
<Courses>
<Course>
<ID>1</ID>
<CourseName>Programming1</CourseName>
</Course>
<Course>
<ID>2</ID>
<CourseName>Programming2</CourseName>
</Course>
</Courses>
</Student>
</Students>
When we have already defined our XML that will serve as the metadata for the templates, the first task we have to do is to create XSD (XML schema) from that XML file. We can do this via the command line tool xsd.exe that ships with Visual Studio. The following command will generate an xsd for you.
C:\Temp>xsd students.xml
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file 'C:\Temp\students.xsd'.
Another way is to use Visual Studio. Open up the desired XML file inside Visual Studio 2005 and press XML>Create Schema.
This will automatically create the xsd schema and you will be also able to view/modify the schema in the designer.
Step 2: Generate Strongly Typed objects from an XSD
Once we have the schema file, we are ready to generate .NET classes from the xsd. You can do this in many ways. Some of the ways may be:
- a. By using xsd.exe that ships with Visual Studio.
- b. By downloading the free Visual Studio addin xsdObjectGen.
- c. By using scgxsd_console.exe that is available with the source code of this article.
The following command will generate .NET classes in your preferred language.
C:\Temp>xsd students.xsd /l:cs /c
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation. All rights reserved.
Writing file 'C:\Temp\students.cs'.
For a full list of command line options for xsd.exe, please refer to this link:
http://msdn2.microsoft.com/en-us/library/x6c1kb0s(vs.71).aspx I will cover scgxsd_console.exe in more detail later in this article.
Step 3: Add Generated classes in SCG Project
Simply add students.cs that is being generated, into your SCG Project. This may look like the following:
Now we are ready to write our SCG Templates with full intellisense support of the XML object in Visual Studio.
Step 4: Write SCG Templates
I have simply defined a property for Students in the "TheProperties.cs", and have a strongly typed representation of the XML schema ready to use in my templates.
private Students studs;
public Students Studs
{
get
{ return studs; }
set { studs = value; }
}
Note here when we write SCG Templates (an ASP.NET UserControl), we have full intellisense support. ASP.NET developers can also use this technique to transform XML into the required format and avoid XSLT. For more details please check my blog post: "
An alternative approach to XSLT with ASP.NET scripts".
Step 5: Read XML and Generate Output
Once you are happy with your SCG templates you can point to any XML file that conforms to your defined xsd schema and generate output. The sample that is available with this article uses the following template to generate html output.
<html>
<body>
<h1>Number of Students <%=Studs.Student.Length%></h1>
<h2>Report Date: <%= DateTime.Now %></h2>
<table border="1" cellpadding="1" cellspacing="1">
<tr><th>#</th><th>
Student</th><th>
Course</th></tr>
<% for (int i = 0; i < Studs.Student.Length; i++)
{ %>
<tr>
<td><%=i%></td>
<td><%= string.Format("{0} {1}",
Studs.Student[i].FirstName, Studs.Student[i].LastName)%></td>
<% for (int j = 0; j < Studs.Student[i].Courses.Length; j++)
{
if (j==0)
{%>
<td><%= Studs.Student[i].Courses[j].CourseName %></td>
<%}
else{%>
<tr><td></td><td></td>
<td><%= Studs.Student[i].Courses[j].CourseName %></td></tr>
<%} %>
<% } %>
</tr>
<% } %>
</table>
</body>
</html>
The generated output is as follows:
The scgxsd_console.exe
scgxsd_console.exe also generates .Net Class from XSD schema. You will be happy to know that the .Net Framework comes with the richness of libraries that support generating .Net class from XSD with a few lines of code and
Mike Hadlow shows the trick in his blog post "
Write your own xsd.exe". I have written the scgxsd_console.exe similarly, also fiddled a bit with xsd.exe in Reflector, which pretty much uses the same code. At a high level the xsd to .Net class creation process goes like the following:
a. Load your xsd file into an XmlSchema.
XmlSchema xsd = XmlSchema.Read(stream, null);
b. Create an XmlSchemaImporter instance that references your schema. This class is used to generate mappings from XSD types to .net types.
XmlSchemaImporter schemaImporter = new XmlSchemaImporter(xsds);
c. Create a CodeDom CodeNamespace instance where you'll build the syntactic structure of your .net types.
CodeNamespace codeNamespace = new CodeNamespace("SmartCodeGen");
d. Create an XmlCodeExporter instance with a reference to the CodeNamespace that you use to export your type. This is the class that actually creates the syntactic structure of the .net types in the CodeNamespace.
XmlCodeExporter codeExporter = new XmlCodeExporter(codeNamespace);
e. Create an XmlTypeMapping instance for each type that you wish to export from the XSD.
Listmaps = new List();
foreach(XmlSchemaType schemaType in xsd.SchemaTypes.Values)
{
maps.Add(schemaImporter.ImportSchemaType(schemaType.QualifiedName));
}
foreach(XmlSchemaElement schemaElement in xsd.Elements.Values)
{
maps.Add(schemaImporter.ImportTypeMapping(schemaElement.QualifiedName));
}
Call the ExportTypeMapping method on XmlCodeExporter for each XmlTypeMapping object, this creates the types syntax in the CodeNamespace object.
foreach(XmlTypeMapping map in maps)
{
codeExporter.ExportTypeMapping(map);
}
I have used the CSharpCodeProvider to output C# source code for the types that were created in CodeNamespace object. But you can also use the VBCodeProvider by adding reference to the Microsoft.VisualBasic.
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
using(StringWriter writer = new StringWriter())
{
codeProvider.GenerateCodeFromNamespace(codeNamespace, writer, new
CodeGeneratorOptions());
string s = (writer.GetStringBuilder().ToString());
using (StreamWriter sw = File.CreateText(_filename))
{
sw.Write(s);
}
}
The complete source code that is described above comes with this article.
Here is an example of how to use "scgxsd_console.exe" to generate .Net class. The following command reads from students.xsd and generates students.cs
scgxsd_console c:\students.xsd c:\students.cs
Generate Business Objects using SmartCodeGenerator
While browsing the internet I found this article "
Business Object for CodeSmith" by JR Hull. This comes with some CodeSmith templates that generate an object layer, a data access layer, and related SQL from a schema defined in the XML. I thought this will be a good and quick example to show that when the XML is converted for use with SCG, it produces the same output. These SCG templates are also available to download with the source code of this article. Note the templates that Hull has written did not use the XmlProperty of CodeSmith and he preferred using the XmlDocument, XmlNodeList, XmlNode objects. I also used the same, which demonstrates an alternative way of retrieving & using XML data in SCG templates. The only point of concern is if we code in this way it will not give intellisense support on our XML schema during development time that I described in my first example. Another thing I must add is I intentionally kept JR Hull's templates in-tact while doing mine, to demonstrate the ease of porting CodeSmith templates to SCG templates.
Here is a little snapshot of the SCGTemplate where it uses XmlNodeList, XmlNode, XmlNode.Attributes etc.
<ItemGroup>
<Compile Include="BusinessLogic\BaseObjectClass.cs" />
<Compile Include="DataAccess\BaseDataClass.cs" />
<%foreach (System.Xml.XmlNode classNode in ClassList)
{
TheTargetClass = classNode.Attributes["name"].Value;%>
<Compile Include="BusinessLogic\<%=TheTargetClass%>.cs" />
<Compile Include="BusinessLogic\<%=TheTargetClass%>Collection.cs" />
<Compile Include="DataAccess\<%=TheTargetClass%>Data.cs" /> <%} %>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<%foreach (System.Xml.XmlNode classNode in ClassList)
{
TheTargetClass = classNode.Attributes["name"].Value;%>
<Content Include="SQL\<%=TheTargetClass %>.sql" /><%}%>
</ItemGroup>
Here is a snapshot of the running project.
Conclusion
SCG is a very powerful code generation framework based on 100% ASP.NET, and in this article I have demonstrated how to use XML as a metadata source for generating templates.However this paper can also be used as a reference by ASP.NET developers who want to transform XML with ASP.NET scripts. With the combination of two powerful technologies, XML and ASP.NET scripts opens up the door to lots of other possibilities. I have not touched XLinq in this article, which is a very interesting subject to look at too. SCG can do much more than just this, so please send me your feedback and let me know what other features you would like to see in SCG.
Thank you for being with me so far and please join the
community and share your SCGTemplates and UIProperties.
Proof reading done by
Christopher Heale.