September 16, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

XML-based Code Generation with CodeSmith

  • August 8, 2005
  • By Mike Gunderloy
  • Send Email »
  • More Articles »

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>





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel