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 |