<?xml version="1.0" encoding="ISO-8859-1"?>
<document url="http://castor.org/howto/how-to-enable-xml-validation.xml">
  <properties>
    <title>How to use XML validation with Castor</title>
    <abstract>
       General information about using XML validation with Castor
    </abstract>
    <status>Final</status>
  </properties>
  <body>
    <title>How to use XML validation with Castor</title>
    <header>
    </header>

    <section title="Intended Audience">

        <p>Anyone who wants to enable XML validation with Castor XML.</p>
        
        <p>This document helps people to get familiar with the basic concepts
        and discusses some implementation details.</p>

        <p>The example given describes the steps required to enble XML validation 
           with Castor XML.</p>
        
    </section>
    
    <section title="Prerequisites">

        <p>None.</p>

        <p>The code given are based on examples from the XML Schema Part 0: Primer 
           Second Edition from <a href="http://www.w3.org/TR/xmlschema-0/#NS">w3.org</a>.</p>

    </section>

    <section title="XML Schema and document instances">

        <p>The XML Schema instance (<tt>po1.xsd</tt>) used here looks as follows:</p>

        <code-panel><![CDATA[
<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>]]></code-panel>

        <p>Surprisingly, the schema isn't complete, so the example XML document 
           <tt>invalid-po1.xml</tt> is actually invalid.</p>
           
        <code-panel><![CDATA[
<?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>]]></code-panel>

        <p>I then corrected the errors (removed the attributes that they didn't
           bother to define in their schema) and created <tt>valid-po1.xml</tt></p>
           
        <code-panel><![CDATA[
<?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>]]></code-panel>
                              
    </section>

    <section title="Java entities">
        
        <p>Now, to bring Castor into the mix, I created two Java classes, 
           <tt>PurchaseOrder</tt> and <tt>Address</tt>.</p>
           
        <code-panel>
public class PurchaseOrder {
   public Address shipTo;
   public Address billTo;
   public String comment;
}

public class Address {
   public String name;
   public String street;
}
        </code-panel>
    </section>

    <section title="Mapping file">
        
        <p>And created a mapping file from the xml to the Java classes.</p>
        
        <code-panel><![CDATA[
<?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>]]></code-panel>

        <p>Note that the mapping file refers to <tt>mapping.xsd</tt>, 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).</p>

        <p>Now for Castor to do validation, the correct castor.properties file must be 
           in the current working directory.</p>
           
        <code-panel>
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
        </code-panel>
        
        <p>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.</p>
           
    </section>

    <section title="Java code to test XML validation">
        
        <p>Finally, I created a driver class to run Castor:</p>
        
        <code-panel>
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();
      }    
   }
}
        </code-panel>
        
        <p>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.</p>

    </section>

    <section title="References">

        <ul>
            <li><a href="http://xerces.apache.org/xerces2-j/">Apache Xerces</a></li>
            <li><a href="http://xerces.apache.org/xerces2-j/features.html">Apache Xerces features</a></li>
        </ul>
        
    </section>
    
    </body>
</document>
