prev table of contents next

5.6 Overriding the Datatype

5.6.1 Replacing the Conversions

Most of the time JAXB's mapping of XML Schema types to Java datatypes will meet your expectations, and the standard conversion of values between binary and string is just what you need. In those rare cases where this is not adequate, it is possible to customize the datatype binding. We'll illustrate this by a simple example where a xsd:simpleType for roman numbers is defined like this:

<xsd:simpleType name="RomanNumberType">
  <xsd:restriction base="xsd:string">
    <xsd:pattern value="M*D?C{,4}L?X{,4}V?I{,4}"/>
    <xsd:minLength value="1"/>
  </xsd:restriction>
</xsd:simpleType>
(The pattern does not cover the subtractive notation which wasn't used in ancient times anyway.) Although the XML type is xsd:string, we'd like to have these values represented by Java's int. This means that we'll also have to supply the conversions between the Roman number as a string of letters and as an integer value. For this, we have to write a simple class like the one given below.
package util.roman;

import java.util.HashMap;
import java.util.Map;

public class RomanNumberConverter {
    private static Map<Character,Integer> rom2int =
        new HashMap<Character,Integer>();
    private static Map<Integer,Character> int2rom =
        new HashMap<Integer,Character>();
    private static int[] digits = new int[]{ 1000, 500, 100, 50, 10, 5, 1 };

    static {
        rom2int.put( 'I',    1 );
        rom2int.put( 'V',    5 );
        rom2int.put( 'X',   10 );
        rom2int.put( 'L',   50 );
        rom2int.put( 'C',  100 );
        rom2int.put( 'D',  500 );
        rom2int.put( 'M', 1000 );
        for( Map.Entry<Character,Integer> entry: rom2int.entrySet() ){
	    int2rom.put( entry.getValue(), entry.getKey() );
	}
    }

    public static int parseStringToInt( String value ){
        int result = 0;
        for( int i = 0; i < value.length(); i++ ){
	    result += rom2int.get( value.charAt( i ) );
	}
        return result;
    }

    public static String printIntToString( int value ){
	StringBuilder sb = new StringBuilder();
        for( int d: digits ){
	    while( value > d ){
		value -= d;
                sb.append( int2rom.get( d ) );
	    }
	}
        return sb.toString();
    }
}
There is a useful class that supports the writing of convertes such as this one: javax.xml.bind.DatatypeConverter provides a rich set of methods that come in handy whenever the XML representation must follow the specifications in
XML Schema Part 2: Datatypes.

The essential methods are the ones we'll have to announce to JAXB, so that it will call our methods for the to and fro between the representations. You may choose any names you like, but the methods must be static. The customizing entry supplied in a bindings file should then look like the one given below, with a threefold nesting of <jaxb:bindings> providing the level where you define the schema position with an XPATH expression.

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               jaxb:version="1.0">
  <jaxb:bindings schemaLocation="roman.xsd" node="/xsd:schema">
    <jaxb:bindings node="//xsd:simpleType[@name='RomanNumberType']">
      <jaxb:javaType name="int"
        parseMethod="util.roman.RomanNumberConverter.parseStringToInt"
        printMethod="util.roman.RomanNumberConverter.printIntToString"/>
    </jaxb:bindings>
  </jaxb:bindings>
</jaxb:bindings>


prev table of contents next