This hack will demonstrate one tool that can generate code from either XML documents or a schema. Relaxer (http://www.relaxer.org) is a free Java tool that, among many other things, can generate Java code from XML documents, RELAX NG schemas (http://www.relaxng.org), or Relax Core schemas (http://www.xml.gr.jp/relax/).
With Relaxer, you can generate Java source code from an XML document or from a RELAX NG schema. In this example, we'll generate code based on the instance time.xml. Assuming that Relaxer is installed and ready to roll [Hack #37], type this command while in the working directory where the book files were extracted:
relaxer -verbose -java -useJAXP time.xml
From this command, Relaxer produces the following five Java files?11,358 lines of code in a matter of seconds:
Time.java
Atomic.java
RStack.java
UJAXP.java
URelaxer.java
Time.java and Atomic.java are based on time.xml. These Java classes provide a set of constructors for creating objects based on this data model. For example, both Time.java and Atomic.java provide a default constructor (no arguments), another constructor that accepts a DOM document as an argument of type org.w3c.dom.Document, and another that accepts a URL argument of type java.net.URL.
Time.java and Atomic.java also provide methods that allow you to access the content of the elements in a document that match this form. For example, you can use the getHour() and setHour() methods to retrieve or change the content of elements. These methods accept arguments of type int. Relaxer chose this type based on its evaluation of the content of the hour element. Another method is makeTextDocument() which outputs a representation of the object as an XML document. The other three Java files were generated automatically by Relaxer and support underlying functionality. The fields and methods in these files are not user-accessible.
To easily view the Java source that Relaxer produces, apply Javadoc to the source files with this command:
javadoc -d relaxerdoc Time.java Atomic.java RStack.java UJAXP.java URelaxer.java
This command puts Javadoc output files in the subdirectory relaxerdoc. To view these files, open the file relaxerdoc/index.html in a browser. Now you can see the documentation for all these classes, including fields, constructors, and methods that Relaxer created.
To compile these Java files, use javac:
javac Time.java
This compiles Time.java, Atomic.java, RStack.java, UJAXP.java, and URelaxer.java all at once. In the working directory, you will find another Java file, ChangeTime.java, shown in Example 7-27. This application uses the Relaxer-generated code to access and set the value of an attribute and the content of elements in time.xml.
import java.io.File; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.SAXException; public class ChangeTime { public static void main(String[ ] args) throws IOException, SAXException, ParserConfigurationException { // Instantiate an Time object Time inst = new Time(new File(args[0])); // Get current System.out.println("Current Time"); System.out.println("Hour: " + inst.getHour()); System.out.println("Minute: " + inst.getMinute()); System.out.println("Second: " + inst.getSecond() + "\n"); // Set content inst.setTimezone("MDT"); inst.setHour(12); inst.setMinute(23); inst.setSecond(05); inst.setMeridiem("p.m."); // Print the new XML System.out.println("New Time"); System.out.println(inst.makeTextDocument()); } }
You can compile and run this program with these commands:
javac ChangeTime.java java ChangeTime time.xml
The output from this program will look like Example 7-28.
Current Time Hour: 11 Minute: 59 Second: 59 New Time <time timezone="MDT"> <hour>12</hour> <minute>23</minute> <second>5</second> <meridiem>p.m.</meridiem> <atomic signal="true"/> </time>
The output you see will be different from this. That's because I went into Time.java and Atomic.java and tweaked the code so it would do what I wanted. For example, I edited the makeTextElement() method in Time.java so that the output would have the line breaks and indentation I wanted.
xmlspy 2004 Enterprise Edition for Windows has the capability of generating Java, C++, and C# classes from XML Schema. You can customize C++ generation to support MSXML, Xerces, and so forth. You can also create projects for Visual Studio .NET (http://msdn.microsoft.com/vstudio/), Borland C# Builder for the Microsoft .NET Framework (http://www.borland.com/csharpbuilder/), and Mono, an open source C# implementation (http://www.go-mono.com/). While we can generate Java and C++ with xmlspy, we'll just illustrate xmlspy's capabilities by generating C#.
To generate C# code, open the XML Schema file moment.xsd in xmlspy from the spycode subdirectory of the working directory (Figure 7-3). This, of course, assumes that you have already downloaded and installed xmlspy 2004 Enterprise Edition and that you have extracted the book's files into your working directory.
Now choose DTD/Schema Generate program code, and a dialog box appears that allows you to choose a code template (Figure 7-4). Click the C# Settings tab and click the Mono Makefile radio button. Then click the Choose Template tab, click the C# radio button as shown in the figure, and then click OK.
You are shown the Browse for Folder dialog box. Navigate to the working directory where you will find the spycode directory. Select it and click OK. The code is generated there. You are then asked if you want to open the directory where the code was just written. If you click Yes, Windows Explorer opens the spycode directory.
Let's have a look at the files that xmlspy produced. Under spycode, you will find the directories Altova, moment, momentTest, and the makefile that xmlspy created. (makefile is for compiling all the files under Mono and we won't be using that, though if you have Mono on your system, you're welcome to use it.) The XML Schema file after which the code was modeled (moment.xsd) is there, plus makeit.bat, moment1.xml, and momentTest.exe, all of which we will discuss in due course.
In the Altova directory are the following files: Altova.dll, AssemblyInfo.cs, Node.cs, Document.cs, and SchemaTypes.cs. In the moment directory are AssemblyInfo.cs, moment.dll, momentDoc.cs, and timeType.cs. And in the momentTest directory are AssemblyInfo.cs, momentTest.cs, and momentTest.exe. I am not going to go into a lot of detail about the code, but I will point out that the code under Altova is undergirding for the other code in moment. The main code that applies to moment.xsd is found in moment, where the timeType.cs code offers many methods for accessing nodes in instances of moment.xsd. The file momentTest.cs in momentTest uses some of these methods, as you will see in Example 7-30.
I won't assume that you have Visual Studio, Mono, or Borland installed, just that you have the Microsoft .NET framework installed [Hack #98] . You will notice another file in spycode called makeit.bat (Example 7-29). This batch file will compile all the C# files that xmlspy created with the csc compiler.
@echo off rem cheater make file for Altova C# files pushd Altova csc /t:library /out:Altova.dll *.cs popd pushd moment csc /t:library /r:../Altova/Altova.dll /out:moment.dll *.cs popd pushd momentTest csc /r:../Altova/Altova.dll /r:../moment/moment.dll *.cs copy momentTest.exe .. popd echo Done
Before building an application, let's go into the momentTest directory, open momentTest.cs in a text editor (Example 7-30), and make the following changes:
Uncomment lines 31, 32, 39, 40, and 46 (i.e., remove the preceding //).
Add lines 33-37 and 41-45 as shown.
// // momentTest.cs // // This file was generated by XMLSPY 2004 Enterprise Edition. // // YOU SHOULD NOT MODIFY THIS FILE, BECAUSE IT WILL BE // OVERWRITTEN WHEN YOU RE-RUN CODE GENERATION. // // Refer to the XMLSPY Documentation for further details. // http://www.altova.com/xmlspy // using System; using Altova.Types; namespace moment { /// <summary> /// Summary description for momentTest. /// </summary> class momentTest { protected static void Example() { // // TODO: // Insert your code here... // // Example code to create and save a structure: momentDoc doc = new momentDoc(); timeType root = new timeType(); SchemaString hour = new SchemaString("10"); SchemaString min = new SchemaString("14"); SchemaString sec = new SchemaString("29"); SchemaString am = new SchemaString("a.m."); SchemaString mdt = new SchemaString("MDT"); // ... doc.SetRootElementName("", "time"); doc.SetSchemaLocation("moment.xsd"); // optional root.Addtimezone(mdt); root.Addhour(hour); root.Addminute(min); root.Addsecond(sec); root.Addmeridiem(am); doc.Save("moment1.xml", root); // // Example code to load and save a structure: // momentDoc doc = new momentDoc(); // timeType root = new timeType(doc.Load("moment1.xml")); // ... // doc.Save("moment1.xml", root); // } /// <summary> /// The main entry point for the application. /// </summary> [STAThread] public static int Main(string[ ] args) { try { Console.WriteLine("moment Test Application"); Example(); Console.WriteLine("OK"); return 0; } catch (Exception e) { Console.WriteLine(e); return 1; } } } }
Now move back up to the parent directory (spycode), and type makeit at a command prompt. If you have entered the changes to momentTest.cs correctly, the build should be successful. makeit also copies the complied file momentTest.exe into the spycode directory. Now type momentTest at the command prompt, and you should get this feedback:
moment Test Application OK
momentTest generated a new copy of moment1.xml (Example 7-31), which is valid with regard to moment.xsd. If you want, you can test it with either xmllint or xsv [Hack #69] .
<?xml version="1.0" encoding="UTF-8"?> <time xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="moment.xsd" timezone="MDT"> <hour>10</hour> <minute>14</minute> <second>29</second> <meridiem>a.m.</meridiem> </time>
xmlspy generated over 1,900 lines of C# code in less than a second or two. This code can quickly be put to good use in manipulating markup programmatically, as you have seen. Have a look at the methods and so forth in timeType.cs under moment, then try experimenting with some of these methods in momentTest.cs, or try generating different kinds of code (Java or C++) with xmlspy based on moment.xsd or some other schema.
.NET Version 1.1 offers several programs, such as wsdl.exe and xsd.exe, that generate C# code based on XML and schema files. Search for wsdl.exe or xsd.exe in the .NET documentation.