Code generator

xml spy
Code generator

XMLSPY 5 now includes a built-in code generator which can automatically generate Java or C++ class files from XML Schema definitions.

XML is not a full programming language in that it cannot be compiled or executed as stand-alone binary executable file; rather XML documents must be bound to an external software application or runtime environment such as a business-to-business application or Web service.

The implementation of any custom XML software application ultimately requires writing programmatic access methods within your code to create, validate, process, transform, modify or perform any in-memory operation on an XML document.

Program coding is most commonly done through the use of high-level XML processing Application Program Interfaces (API) such as Microsoft MSXML and Apache Xerces, freely available for various programming languages.

Top Next

© 2002 Altova


Introduction to code generator

Introduction to code generator

The XMLSPY 5 code generator's default templates automatically generates class definitions corresponding to all declared elements or complex types which redefine any complex type in your XML Schema, preserving the class derivation as defined by extensions of complex types in your XML Schema.

In the case of complex schemas which import schema components from multiple namespaces, XMLSPY 5 preserves this information by generating the appropriate C++ namespaces or Java packages.

Additional code is implemented, such as functions which read XML files into a Document Object Model (DOM) in-memory representation, write XML files from a DOM representation back to a system file, as well as XML validation and transformation.

The output program code is expressed in C++ or Java programming languages. The C++ generated output uses MSXML 4.0 and includes a Visual Studio 6.0 project file; the generated Java output is written against the industry-standard Java API for XML Parsing (JAXP) and includes a Sun Forte for Java project file.

Code generator templates
Output code is completely customizable via a simple yet powerful template language which gives full control in mapping XML Schema built-in data-types to the primitive datatypes of a particular programming language. It allows you to easily replace the underlying parsing and validating engine, customize code according to your company's writing conventions, or use different base libraries such as Microsoft Foundation Classes (MFC) and Standard Template Library (STL).

Additionally you can build your own templates to automate the generation of anything imaginable, for example: EJB's, WSDL files, SQL scripts, ASP or WML code. Future releases and updates will include additional language templates to generate different code modules, and will support additional programming languages such as Microsoft C# and Visual Basic.NET.

XMLSPY 5's schema editor is well suited as a software modeling and prototyping tool, allowing XML applications to be rapidly prototyped at a high level in XML Schema and then automatically generated. Changes to an application's XML Schema content model can be immediately reconciled with a software implementation simply by re-running the code generator.

Built-in code generation frees software developers from the mundane task writing low level infrastructure code, enabling them to focus on implementing critical business logic. By automatically generating a programming language binding, XMLSPY 5 accelerates project development time from initial design to final implementation, resulting in substantial cost savings and time to market advantages.

Previous Top Next


Generating source code from a schema

Generating source code from a schema

The following sequence shows how to generate and compile the source code produced by the AddressLast schema file as the input.

1. Open the schema you want to generate source code for (e.g. AddressLast).

gen-vs01

2. Select the menu item DTD/Schema | Generate Program Code....
3. Select the type of code you want to generate, Java or C++, (e.g. cpp.spl or java.spl)
4. Select folder and file name (AddressLast.dsw) in the Save as dialog box, and click Save.
If you are producing Java code, only the select folder dialog appears.
5. You are prompted to open the newly created project in Microsoft Visual Studio, click OK.
If Java code is produced, you are prompted to open the corresponding output directory.

gen-vs1

6. Select the menu option Build | Build AddressLast.exe.

gen-vs2

7. The code is compiled and linked.

gen-vs3

8. Hit the F5 shorcut key, or click the Go icon, to start the file.
9. A message appears stating the lack of debugging info, click OK to continue.

The AddressLast.exe file is compiled successfully.
gen-vs4


Example usage of generated Java and C++ code

Example usage of generated Java and C++ code

Using the generated classes
The previous sections explained how to open a schema, choose a template and generate the files you need. You then open the generated project or the corresponding directory and get down to work.

To keep things simple, we will work with a very small xsd file, the source code is shown below. Save this as "test10.xsd" if you want to get the same results as our example:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="Test_Empty">
<xs:annotation>
<xs:documentation>Test_Complex_Type_Element_Attriubute</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element ref="internationalPrice"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="internationalPrice">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="currency" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>

We will only use this schema file in this example, it will therefore be easy to figure out the differences between the different programming languages in the following code examples.

Please note that the generated classes will be named differently from xsd to xsd file. When working with your own schemas or other samples, make sure you adapt the names of the functions and variables.


Creating XML files

Creating XML files

Java:
To give a general overview, here is the code to create a test file:

protected static void CreateNewFileAndSave() {

Test_EmptyType testEmpty = new Test_EmptyType();

internationalPriceType price = new internationalPriceType();
price.addcurrency( new SchemaString( "USD" ) );
price.setValue( new SchemaInteger( 25 ) );

testEmpty.addinternationalPrice( price );

price = new internationalPriceType();
price.addcurrency( new SchemaString( "EUR" ) );
price.setValue( new SchemaInteger( 13 ) );

testEmpty.addinternationalPrice( price );

Document doc = new Document();
doc.setRootElementName( "C:\\test.xsd", "Test_Empty");
doc.save("c:\\test.xml", testEmpty);
}

the main function will be modified slightly to call the test function, and will look like this:

public static void main(String args[]) {
try {
System.out.println("testmodule Test Application");
CreateNewFileAndSave();

System.out.println("OK");
} catch (Exception e) {
e.printStackTrace();
}
}

The idea of the generated classes is the following:
Generate the tree you want to save in the new file, create a document and store the tree in it.

Th first line:
Test_EmptyType testEmpty = new Test_EmptyType();

Test_EmptyType is a class that was generated. It is similar to the <xs:element name="Test_Empty"> in the schema file. This is the root element for the xml files being generated (as in generate sample xml file).

We now have a new emptytype. All we have to do is fill it with the internationalprice. Look at the schema again and notice that prices are stored as a sequence within the emptytypes.

The next step is to make an object of internationalPriceType and fill the new object.

Definition of internationalPrice in the schema file:

<xs:element name="internationalPrice">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="currency" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>

We will first insert the currency attribute, using Dollars to store the price.

price.addcurrency( new SchemaString( "USD" ) );

Please remember to use the SchemaString class - or compiler errors will occur.

We now have to set a value, how much the item costs in Dollars, e.g. 25.
price.setValue( new SchemaInteger( 25 ) );

We filled the internationalPriceType, and we now have to add it to the sequence of internationalPriceTypes within Test_Empty:

testEmpty.addinternationalPrice( price );

Done, the tree is complete! All we have to do now, is create a new document and define the namespace and name of the root element:
Document doc = new Document();
doc.setRootElementName( "C:\\test.xsd", "Test_Empty" );

Finally, save the file, - the first parameter is the file name, the second is the root element itself.
doc.save("c:\\test.xml", testEmpty);

C++:

The C++ implementation is very similar to the Java one, we will therefore only discuss the differences between the two.

The function to be inserted above the main function is as follows:

void SampleCreate()
{
CTest10Doc doc;

CTest_EmptyType testEmpty;

CinternationalPriceType price( CSchemaDecimal( 25 ) );
CSchemaString sDollarText = _T("USD");

price.Addcurrency( sDollarText );

testEmpty.AddinternationalPrice( price );

CinternationalPriceType priceEur( CSchemaDecimal( 13 ) );
CSchemaString sEuroText = _T("EUR");

priceEur.Addcurrency( sEuroText );

testEmpty.AddinternationalPrice( priceEur );

doc.SetRootElementName( _T("C:\\codegen\\test.xsd"), _T("Test_Empty") );
doc.Save( _T("c:\\testcpp.xml"), testEmpty);
}

As in the Java code, you have to modify the main function to call your SampleCreate()
…..
try
{
CoInitialize(NULL);
SampleCreate();
CoUninitialize();
return 0;
}
….

First off, you'll notice that all classes begin with a capital C - the rest of the class naming stays the same. All member function names have to start with a capital letter.

Second, a new CSchemastring was allocated in a seperate line, whereas in java, this was done in the addcurrency call itself. This just shows an alternative method of achieving the same thing.

A further change is how the value of the internationalPrice element is initialized, the value is supplied in the constructor.

Please take care with the _T macros. The macro ensures that the character constant, is stored correctly, whether you're compiling for Unicode or non Unicode programs.


Opening and parsing existing XML files

Opening and parsing existing XML files

We will now use the test.xml file generated with our sample program. Please make sure that this file exists, or change the path to it in the load command.

Java:
Sample source code:

protected static void OpenFileParseAndValidate() {
Document doc = new Document();
Test_EmptyType testEmpty = new Test_EmptyType( doc.load( "c:\\test.xml" ) );

System.out.println( "document loaded...." );
System.out.println( "found " + testEmpty.getinternationalPriceCount() + " price item(s)..." );

if ( testEmpty.hasinternationalPrice() ) {
for ( int i = 0; i < testEmpty.getinternationalPriceCount(); i++ ) {
internationalPriceType price = testEmpty.getinternationalPriceAt( i );
System.out.println( " internationalPriceType number " + (i+1) + ": " + price.getcurrency().asString() );
}
System.out.println( "Finished dumping objects!" );
}
}

in the main() function exchange:

CreateNewFileAndSave();
with,
OpenFileParseAndValidate();

The rest is reasonably straightforward.
First create a new variable of type Document, and one for the root element, which is immediately filled by the doc.load function. The only parameter it needs, is the path to the xml file being used.

Document doc = new Document();
Test_EmptyType testEmpty = new Test_EmptyType( doc.load( "c:\\test.xml" ) );

We now have a document and know that Test_Empty contains a sequence of internationalPrice. We still need to get the number of elements in the sequence using:
testEmpty.getinternationalPriceCount();

If you cannot find this call in the source code, you will find it in one of the System.out.println instructions.

The next step is to dump the values of all of the internationalPrice elements, first checking if any exist:
if ( testEmpty.hasinternationalPrice() )

Why check for objects? The call just shows that a function exists, that checks this for you.

Step through the internationalPrices and dump their values:

for ( int i = 0; i < testEmpty.getinternationalPriceCount(); i++ ) {
internationalPriceType price = testEmpty.getinternationalPriceAt( i );
System.out.println( " internationalPriceType number " + (i+1) + ": " + price.getcurrency().asString() );
}

We're finished, all the values are listed in your output window.

C++:
This section only explains the differences to the java code.

void SampleLoad()
{
CTest10Doc doc;

CTest_EmptyType testEmpty = doc.Load(_T("c:\\testcpp.xml"));
TRACE( _T("document loaded....\r\n") );

CString h;
h.Format( _T("found %d price item(s)...\r\n" ), testEmpty.GetinternationalPriceCount() );
TRACE( h );

if ( testEmpty.HasinternationalPrice() )
{
for ( int i = 0; i < testEmpty.GetinternationalPriceCount(); i++ )
{
CinternationalPriceType price = testEmpty.GetinternationalPriceAt( i );

h.Format( _T( " internationalPriceType number %d: %s %s\r\n"), (i+1), (LPCTSTR)(CString) price.Getcurrency(), (LPCTSTR)(CString) price.GetValue() );
TRACE( h );
}
TRACE (_T( "Finished dumping objects!\r\n" ));
}
}

All member functions start with a capital letter, and all Classnames start with a C.

Typecasts for formatting an output string are needed. You can always cast a Schematype (standard schema types: string, integer, decimal, … ) to a CString. The format statement does not take a CString however, this is why you have to do 2 typecasts.


The way to SPL (Spy Programming Language)

The way to SPL (Spy Programming Language)

This section gives an overview of the Spy Programming Language.

It is assumed that you have prior programming experience, and are familiar with operators, functions, variables and classes, as well as the basics of object oriented programming - which is used heavily in spl. You should also have detailed knowledge of XML Schema.

An example c++ (and java) cpp.spl is supplied in the ..\xmlspy\spl folder. You can use this file as an aid to help you in develping your own templates.

How code generator works:
The basis of the generator are the template files (.spl). The template file is interpreted by the code generator and outputs out a .cpp, .java source code file (or project), or any other type of file depending on the template. The source code is then compiled into an .exe file which can then be started. The .exe file then accesses the XML data described by the schema file.

Spl files have access to a wide variety of information that is collated from the source schemas. Please note that a spl file is not tied to a specific schema, but allows access to all schemas! Make sure you write you spl files generically, avoid structures etc. which apply to specific schemas!

Creating a new file in spl:

[create "test.cpp"]
#include "stdafx.h"
[close]

This is a very basic spl. It creates a file named test.cpp, and places the include statement within it. The close command completes the template.


Code Blocks

Code Blocks

Code generator instructions are enclosed in square brackets '[' and ']'.

Multiple instructions can be included in a bracket pair, additional instructions have to be separated by a new line or a colon ':'.

Valid examples are:

[$x = 0
$x = $x + 1]

or

[$x = 0: $x = $x +1]


Comments

Comments

Comments always begin with a ' character, and terminate on the next line, or at a block close character ].


Variables

Variables

The $ character is used when declaring or using a variable, a variable is always prefixed by $.

Variables types:
· integer
· string
· class iterator (see foreach statement)

Variable types are declared by first usage,

[$x = 0]
x, is now a integer

[$x = "teststring"]
x, is now treated as a string

Working with strings:
The previous example shows how a fixed string is assigned to a variable. You will also need to concatenate strings, as well as assign them to different variables.

Assigning a string to a variable:

[$x = $module]

Defining string concatenation uses the & character:

[$x = "testconcat" & "ination" & $module]


Global objects

Global objects

After a Schema file is analyzed, by the code generator, the objects visible in the table below exist in the Template Engine.

Name | Type | Description
$namespaces | Namespace collection | Collection of Namespace objects
$classes | Class collection | All the complextypes, elements,… in a flat view. The order is like the order while parsing through the types
$module | string | name of the source Schema without extension
$outputpath | string | The output path specified by the user, or the default output path


Using files

Using files

[create filename]
creates a new file - the file has to be closed with the [close] instruction.

[append filename]
appends items to a specific file - the file has to be closed with the [close] instruction.


Conditions

Conditions

adding conditions to your code (if):
Spl allows you to use standart "if" instructions - the syntax is as follows:

[if condition]

[instruction block]

[else]

[instruction block]

[endif]

or, without else:

[if condition]

[instruction block]

[endif]

Please note that there are no round brackets enclosing the condition!

Conditions:
As in any other programming language conditions are constructed with logical and comparison - operators which are:

Logical operators:

Operator | Operator in spl
Not | not
And | and
Or | or

Comparison operators:

Not equal | <>
Equal | =
Greater than | >
Less than | <

A sample if instruction with a condition looks like this:

[if $namespace.ContainsPublicClasses and $namespace.Prefix <> ""]
whatever you want ['inserts whatever you want, in the resulting file]
[endif]


foreach

foreach

Vectors and iterators

A vector is a collection of objects - like a ordinary array. Iterators solve the problem of storing and incrementing array indexes when accessing objects.

foreach example:

[foreach $class in $classes]

[if not $class.IsInternal]

class [=$class.Name];

[endif]

[next]

The first line:
$classes is the global object of all types. $classes is also a vector - a collection of single class objects.

Foreach steps through all the items in $classes, and executes the code within the instruction.

In each iteration, $class is assigned to the next class object. You simply work with the class object instead of using, classes[i]->class->Name(), as you would in C++.

foreach is executed till the [next] instruction.


Adding text to files

Adding text to files

Iterating through collections and incrementing variables will not generate compilable files.

Text not enclosed by [ and ], is written directly to the file. It's similar to using javascript in ASP.

If you want to place the value of a variable in the result document, use the following method:

The result of your calculation is [=$x] - so have a nice day…

- assuming that the value of variable x is 23, the file output will be:

The result of your calculation is 23 - so have a nice day…


Built in Types

Built in Types

The section describes the built-in types used in global variables which describe the parsed schema.


Namespace

Namespace

Namespace abstraction:

Name | Type | Description
URI | string | The URI of this namespace
Prefix | string | The prefix of this namespace
Classes | Class collection | Collection of the classes in this namespace - step through them using foreach


Class

Class

Class abstraction:

Name | Type | Description
NamespacePrefix | string | Prefix of the namespace in which the class is
NamespaceURI | string | URI of the namespace in which the class is
Name | string | Name of the type in the resulting file
SchemaName | string | Name of the type as in the original schema file
Base | string | Name Base type
SchemaBase | string | Name of the base type as in the original schema file
BuiltInBase | string | Name of the root simple type
BuiltInSchemaBase | string | Name of the root simple type as in the original schema file
IsSimpleType | boolean | true if this is a simple type
IsComplexType | boolean | true if this is a complex type
Description | string |
IsGlobal | boolean | true if this type is usable anywhere in the generated files
IsAnonymous | boolean | true if this type is a kind of a help object that will not be shown in the generated files
IsInternal | boolean | true if this is a help object dealing with root elements
Members | Member collection |
Facets | Facet collection |


Member

Member

Abstraction of a member-variable inside a class.

Name | Type | Description
Name | string | Name in the resulting file
SchemaName | string | Name as in the original schema file
Type | string |
SchemaType | string |
TypeObject | Class | See explanation below *
HasTypeObject | boolean | true, when typeobject has a valid value
Description | string |
IsBuiltInType | boolean | true, ifn this is a schema type, e.g. xsd:string, int,...
IsElement | boolean | true, if this is an element
IsAttribute | boolean | true, if this is an attribute
MinOcc | integer | Minoccur, as in schema
MaxOcc | integer | Maxoccur, as in schema

*TypeObject:
The reason this has to be used is the following: you might use a complextype, and the complextype itself contains a simpletype.

· Get the $class variable, and step through its members using foreach - at some point you arrive at the simpletype.

· You now need the simpletype properties: type name, or whatever.

· Check the HasTypeObject, if its value is true, the member has been filled with valid contents and can be parsed.


Facet

Facet

This class consists of the constraint itself, and a several boolean variables.

The boolean variables tell you the kind of constraint you're currently dealing with. The names of the Boolean variables are identical to those used in the schema specification.

Name | Type | Description
Constraint | string | Holds the value of the facet
IsLength | boolean |
IsMinLength | boolean |
IsMaxLength | boolean |
IsMinInclusive | boolean |
IsMinExclusive | boolean |
IsMaxInclusive | boolean |
IsMaxExclusive | boolean |


Error Codes

Error Codes

Operating System Error Codes
201 File not found: '%s'
202 Cannot create file '%s'
203 Cannot open file '%s'

Schema Error Codes
301 Failed to initialize grammar file
302 Validator: %s
303 Validator cannot load schema '%s'
304 Cannot create validator

Syntax Error Codes
401 Keyword expected
402 '%s' expected
403 No output file specified

Runtime Error Codes
501 Unknown variable '%s'
502 Redefinition of variable '%s'
503 Variable '%s' is not a container
504 Unknown property '%s'
505 Cannot convert from %s to %s

Previous Top