How to use XML validation with Castor Intended Audience Prerequisites XML Schema and document instances Java entities Mapping file Java code to test XML validation References
Intended Audience
Anyone who wants to enable XML validation with Castor XML.
This document helps people to get familiar with the basic concepts
and discusses some implementation details.
The example given describes the steps required to enble XML validation
with Castor XML.
Prerequisites
None.
The code given are based on examples from the XML Schema Part 0: Primer
Second Edition from w3.org.
XML Schema and document instances
The XML Schema instance (po1.xsd) used here looks as follows:
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:po="http://www.example.com/PO1"
targetNamespace="http://www.example.com/PO1"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<element name="purchaseOrder" type="po:PurchaseOrderType"/>
<element name="comment" type="string"/>
<complexType name="PurchaseOrderType">
<sequence>
<element name="shipTo" type="po:USAddress"/>
<element name="billTo" type="po:USAddress"/>
<element ref="po:comment" minOccurs="0"/>
<!-- etc. -->
</sequence>
<!-- etc. -->
</complexType>
<complexType name="USAddress">
<sequence>
<element name="name" type="string"/>
<element name="street" type="string"/>
<!-- etc. -->
</sequence>
</complexType>
<!-- etc. -->
</schema> |
|
Surprisingly, the schema isn't complete, so the example XML document
invalid-po1.xml is actually invalid.
<?xml version="1.0"?>
<apo:purchaseOrder
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/PO1 po1.xsd"
xmlns:apo="http://www.example.com/PO1"
orderDate="1999-10-20">
<apo:shipTo country="US">
<apo:name>Alice Smith</apo:name>
<apo:street>123 Maple Street</apo:street>
<!-- etc. -->
</apo:shipTo>
<apo:billTo country="US">
<apo:name>Robert Smith</apo:name>
<apo:street>8 Oak Avenue</apo:street>
<!-- etc. -->
</apo:billTo>
<apo:comment>Hurry, my lawn is going wild</apo:comment>
<!-- etc. -->
</apo:purchaseOrder> |
|
I then corrected the errors (removed the attributes that they didn't
bother to define in their schema) and created valid-po1.xml
<?xml version="1.0"?>
<apo:purchaseOrder
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/PO1 po1.xsd"
xmlns:apo="http://www.example.com/PO1">
<apo:shipTo>
<apo:name>Alice Smith</apo:name>
<apo:street>123 Maple Street</apo:street>
<!-- etc. -->
</apo:shipTo>
<apo:billTo>
<apo:name>Robert Smith</apo:name>
<apo:street>8 Oak Avenue</apo:street>
<!-- etc. -->
</apo:billTo>
<apo:comment>Hurry, my lawn is going wild</apo:comment>
<!-- etc. -->
</apo:purchaseOrder> |
|
Java entities
Now, to bring Castor into the mix, I created two Java classes,
PurchaseOrder and Address.
public class PurchaseOrder {
public Address shipTo;
public Address billTo;
public String comment;
}
public class Address {
public String name;
public String street;
}
|
|
Mapping file
And created a mapping file from the xml to the Java classes.
<?xml version="1.0"?>
<mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://castor.exolab.org/"
xmlns:apo="http://www.example.com/PO1"
xsi:schemaLocation="http://castor.exolab.org/ mapping.xsd">
<class name="PurchaseOrder">
<map-to xml="purchaseOrder" ns-uri="http://www.example.com/PO1"/>
<field name="shipTo" type="Address" direct="true">
<bind-xml name="apo:shipTo" />
</field>
<field name="billTo" type="Address" direct="true">
<bind-xml name="apo:billTo" />
</field>
<field name="comment" type="string" direct="true">
<bind-xml name="apo:comment" />
</field>
</class>
<class name="Address">
<field name="name" type="string" direct="true">
<bind-xml name="apo:name" />
</field>
<field name="street" type="string" direct="true">
<bind-xml name="apo:street" />
</field>
</class>
</mapping> |
|
Note that the mapping file refers to mapping.xsd, which can be found
in the Castor JAR file, as the schema for the Castor namespace. I'm
also using relative paths for all the schemas, so the xml files and
the schemas must all reside in current working directory (the directory
from which you call java).
Now for Castor to do validation, the correct castor.properties file must be
in the current working directory.
org.exolab.castor.indent=true
org.exolab.castor.parser.namespaces=true
org.exolab.castor.sax.features=http://xml.org/sax/features/validation,\
http://apache.org/xml/features/validation/schema,\
http://apache.org/xml/features/validation/schema-full-checking
|
|
The indent property is just to make the output XML easy to read, and
because I'm using Xerces, the apache.org properties come into the
mix. Other XML parsers will probably have different flags that need to
be set.
Java code to test XML validation
Finally, I created a driver class to run Castor:
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
public class ValidationDriver {
public static void main( String[] args ) {
String filename = args[0];
try {
Mapping myMap = new Mapping();
myMap.loadMapping( "po1Map.xml" );
Unmarshaller um1 = new Unmarshaller( myMap );
PurchaseOrder po1 =
(PurchaseOrder)um1.unmarshal(new FileReader(filename));
StringWriter myWriter = new StringWriter();
Marshaller m1 = new Marshaller( myWriter );
m1.setMapping( myMap );
m1.setNamespaceMapping("", "http://www.example.com/PO1");
m1.setSchemaLocation("http://www.example.com/PO1 po1.xsd");
m1.marshal( po1 );
System.out.println( "Castor Output:" );
System.out.println( myWriter.getBuffer().toString() );
System.out.println( "" );
StringReader myReader =
new StringReader(myWriter.getBuffer().toString());
PurchaseOrder po2 =
(PurchaseOrder)um1.unmarshal( myReader );
System.out.println( "Comment from reconstructed class:" );
System.out.println( po2.comment );
}
catch( IOException e ) {
e.printStackTrace();
}
catch( MarshalException e ) {
e.printStackTrace();
}
catch( ValidationException e ) {
e.printStackTrace();
}
catch( MappingException e ) {
e.printStackTrace();
}
}
}
|
|
With Castor, Xerces, and Commons-Logging in the classpath, one can run
this ValidationDriver and pass in an xml filename (valid-po1.xml or
invalid-po1.xml). The invalid file will print out an exception stack
trace that is due to a Xerces validation error. The valid xml should
produce the roundtrip xml (that's xml->Java->xml), and the comment
from the purchase order from the Java object (after it has gone
xml->Java->xml->Java). Note that the xml is being validated against
the schema each time it is going from xml->Java (though with this
example there is no validation going from Java->xml). The extra round
trips might seem excesive, but they helped me work out some kinks in
my mapping file when I had to do this the first time.
References
|