http://www.developer.com/

Back to article

XML-based Code Generation with CodeSmith


August 8, 2005

As you undoubtedly know, XML has become ubiquitous as a data storage and transmission format. It's possible to build an entire computing infrastructure on top of XML files, and many development shops have done just that. XML-aware tools are an important part of the picture, and in this article I'll demonstrate how one such tool, the CodeSmith code generator, can use XML files as input. Because CodeSmith templates can contain as much programming logic as you need, this opens up possibilities for transforming XML in ways that would be difficult or impossible using conventional XML tools.

CodeSmith Overview

CodeSmith is a template-driven code generator. CodeSmith templates are written in a language similar to ASP.NET, and may contain scripting code in C#, VB, or JScript. When you use CodeSmith to generate code, the CodeSmith engine combines static content in the template with dynamic metadata that can be supplied in a variety of formats. In particular, CodeSmith can make use of an XML file as a metadata source for a template. This allows you to supply the dynamic part of your generated code in the form of an XML file that gets read and processed by the CodeSmith engine.

The XmlProperty Directive

To use XML metadata in a template, you use an XmlProperty directive. This directive creates a property of the template that allows the end user to choose an XML file at runtime. The XmlProperty directive has six possible attributes:

  • Name - The Name attribute is used as the name of the property when it is displayed on the template's property sheet in CodeSmith. This is also the variable name that is used to store the value of the property within the template.
  • Schema - The Schema attribute specifies an XSD schema to be used to parse the XML file chosen by the user at runtime. This attribute is optional, but I strongly recommend that you supply a schema, because this enables strong typing and IntelliSense within the template.
  • Default - The optional Default attribute is used to set the default value for this property.
  • Category - The optional Category attribute specifies what category this property should appear under in the CodeSmith Explorer property sheet.
  • Description - The optional Description attribute supplies descriptive text to be displayed at the bottom of the property sheet when this property is selected.
  • Optional - The Optional attribute specifies whether or not this property is optional.

So much for the syntax - let's see an example!

From XML to HTML

For this example, I'm going to use CodeSmith to transform a simple XML file that might be generated by an automated testing tool into a friendlier HTML report. Yes, there are other ways to turn XML into HTML (notably the use of XSL). But with the flexibility of CodeSmith's templates, it's easy to do some things (like perform calculations) that are difficult in pure XSL. Also, this example demonstrates the mechanics of working with XML metadata without the complexity of generating a source code for a full-blown application. One of the key things to keep in mind about a general-purpose code generator is that you can use it to generate any text, not just programming code: SQL statements, HTML pages, and even documentation are all perfectly reasonably outputs as long as you can find patterns in them.

So, here's what the raw XML I'm starting with looks like. This is the sort of thing that computers like to see, because they can parse tag soup easily, but it's not so handy for people:


<?xml version="1.0" encoding="UTF-8"?>
<TestReport xmlns="http://www.example.com/testing"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ProjectName>Inventory Project</ProjectName>
  <TestDate>2005-08-05</TestDate>
  <Tests>
    <Test TestNumber="1" TestName="Basic data 
entry" Passed="1"/>
    <Test TestNumber="2" TestName="Advanced data 
entry" Passed="0"/>
    <Test TestNumber="3" TestName="Basic reporting" 
Passed="1"/>
    <Test TestNumber="4" TestName="Advanced 
reporting" Passed="1"/>
    <Test TestNumber="5" TestName="Inventory 
reconciliation" Passed="0"/> 
  </Tests>
</TestReport>

To make effective use of this XML file (and others with the same structure) as metadata in CodeSmith, we'll need the corresponding XSD file, TestReport.xsd:


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://www.example.com/testing"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.com/testing"
elementFormDefault="qualified" attributeFormDefault="unqualified">
 <xs:element name="TestReport">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="ProjectName" 
type="xs:string"/>
    <xs:element name="TestDate" type="xs:date"/>
    <xs:element name="Tests">
     <xs:complexType>
      <xs:sequence>
       <xs:element name="Test" 
maxOccurs="unbounded"> <xs:complexType>
        <xs:attribute name="TestNumber" 
type="xs:integer" use="required"/>
        <xs:attribute name="TestName" 
type="xs:string" use="required"/>
        <xs:attribute name="Passed" 
type="xs:boolean"
use="required"/> </xs:complexType>
       </xs:element>
      </xs:sequence>
     </xs:complexType>
    </xs:element>
   </xs:sequence>
  </xs:complexType>
 </xs:element>
</xs:schema>

Armed with the schema file, I created a new CodeSmith template in the CodeSmith Studio IDE. Figure 1 shows an early stage in writing this template. As you can see, CodeSmith can use the information in the TestReport.xsd schema file (specified in the XmlProperty directive) to provide IntelliSense-style help in the IDE. This makes it very easy to get the proper element and attribute names from the XML file into the template.



Click here for a larger image.

Here's the finished template, which will rearrange information from the XML file into an HTML file, as well as performing a simple calculation on the way:


<%@ CodeTemplate Language="C#" TargetLanguage="HTML" Description="Generate friendly testing report" %> <%@ XmlProperty Name="TestReport" Schema="TestReport.xsd" Optional="False" Category="Data" Description="Raw test results." %> <html> <head> <title>Test Report</title> </head> <body> <h1><%= TestReport.ProjectName %></h1> <h2>Test Date: <%= TestReport.TestDate %></h2> <h3>Results:</h3> <table border="1" cellpadding="1" cellspacing="1"> <tr><th>#</th><th>Test</th><th>Result</th></tr> <% int passedTests = 0; %> <% for (int i = 0; i < TestReport.Tests.Count; i++) { %> <tr><td><%= TestReport.Tests[i].TestNumber %></td> <td><%= TestReport.Tests[i].TestName %></td> <% if (TestReport.Tests[i].Passed) { passedTests++; %> <td>Passed</td> <% } else { %> <td>Failed</td> <% } %></tr> <% } %> </table> <p> <p>Passed <%= passedTests %> out of <%= TestReport.Tests.Count %> Tests (<%= (passedTests*100)/TestReport.Tests.Count %>%).</p> </body> </html>

Generating the Report

The template may look like a step backwards in our quest to simplify things for human consumption, but this is the programming part of the job. Ideally, you only develop a code template once, and then you use it over and over again. But if you tease apart the HTML code (which CodeSmith copies to the output) from the scripting code (which CodeSmith runs) it's really not that complex. <%= %> token pairs hold variables that CodeSmith evaluates at runtime; the result of the evaluation is what gets written to the output. <% %> token pairs contain scripting code (in C# in this particular template) that the CodeSmith engine runs. Everything else just gets copied over intact.

At run time, the TestReport property will display a builder button in the CodeSmith user interface. Clicking this button opens a file open dialog box which allows the user to browse for an appropriate XML file to use as a metadata source:



Click here for a larger image.

CodeSmith will display all the XML files in the folder and let the user choose any one of them to use as the source for the template's metadata, but an attempt to use a file that doesn't match the specified schema will result in an error. Selecting an appropriate XML file lets you proceed with code generation. For example, using the XML file that you saw earlier in the article yields this output:


<html>
<head>
<title>Test Report</title>
</head>
<body>
<h1>Inventory Project</h1>
<h2>Test Date: 8/5/2005 12:00:00 AM</h2>
<h3>Results:</h3>
<table border="1" cellpadding="1" cellspacing="1">
<tr><th>#</th><th>Test</th><th>Result</th></tr>
<tr><td>1</td>
<td>Basic data entry</td>
<td>Passed</td>
</tr>
<tr><td>2</td>
<td>Advanced data entry</td>
<td>Failed</td>
</tr>
<tr><td>3</td>
<td>Basic reporting</td>
<td>Passed</td>
</tr>
<tr><td>4</td>
<td>Advanced reporting</td>
<td>Passed</td>
</tr>
<tr><td>5</td>
<td>Inventory reconciliation</td>
<td>Failed</td>
</tr>
</table>
<p>
<p>Passed 3 out of 5 Tests 
(60%).</p>
</body>
</html>

Of course, it looks better in a browser:

The generated test report

Working Smarter is the Key

The key to using XML effectively is to remember that it's a tool. Putting your data into XML files should not be an end in itself - a fact which some developers seem to lose sight of in their drive to become checklist-compliant. Tools such as CodeSmith can help you unlock the data in XML files and make it a useful part of your development process. If your organization has standardized on XML files as a place to store data, you can use CodeSmith to get that data back out and build useful files, tools, and artifacts with it. What more could anyone ask of a developer?

About the Author

Mike Gunderloy is the author of over 20 books and numerous articles on development topics, and the lead developer for Larkware. Check out his latest books, Coder to Developer and Developer to Designer, both from Sybex. When he's not writing code, Mike putters in the garden on his farm in eastern Washington state.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date