Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I specify the adapter(s) which JAXB uses for marshaling/unmarshaling data?

Tags:

java

xml

jaxb

Is there a way to specify the Adapter which JAXB uses for marshaling/unmarshaling objects in my XML schema?

For example, if I want to parse the following as an integer:

<SpecialValue>0x1234</SpecialValue>

I can use the following in my schema:

<xs:simpleType name="HexInt">
    <xs:annotation> 
        <xs:appinfo>
            <jaxb:javaType name="int" parseMethod="Integer.decode" />
        </xs:appinfo>
    </xs:annotation>  
    <xs:restriction base="xs:string">
        <xs:pattern value="0x[0-9a-fA-F]+" />
    </xs:restriction>
</xs:simpleType>
<xs:element name="SpecialValue" type="HexInt" />

When I run the schema through the XJC tool, the String "0x1234" should be decoded using Integer.decode() as the Integer with value 0x1234, or 4660 in decimal. The Adapter class that is generated reflects this properly:

public Integer unmarshal(String value) {
    return (Integer.decode(value));
}

However, when I want to marshal the value back to an XML element (which is a String literal), the Adapter class does the following:

public String marshal(Integer value) {
    if (value == null) {
        return null;
    }
    return value.toString();
}

In this case, the String value of the integer 0x1234 (4660 in decimal) is "4660", which does not adhere to my schema (because it has no "0x" prefix).

How can I tell the Adapter that I want the integer 0x1234 to be marshalled as the "0x1234" String literal? I would be like to be able to do this within the schema so that I can just generate new Java classes without having to modify the output. Is this possible?

Edit: Based on the accepted answer, here is what I did to get this working:

I wrote a method in a class com.example.Parse called toHexString():

public static String toHexString(int value)
{
    return ("0x" + Integer.toHexString(value));
}

Then I pointed my schema to that method for printing:

<xs:simpleType name="HexInt">
    <xs:annotation> 
        <xs:appinfo>
            <jaxb:javaType name="int" parseMethod="Integer.decode" printMethod="com.example.Parse.toHexString" />
        </xs:appinfo>
    </xs:annotation>  
    <xs:restriction base="xs:string">
        <xs:pattern value="0x[0-9a-fA-F]+" />
    </xs:restriction>
</xs:simpleType>
like image 855
troyal Avatar asked May 10 '11 23:05

troyal


1 Answers

Very similar problem: "How to map String coordinates to java.awt.Point ?"

(0) Schema

<xsd:simpleType name="Point">
    <xsd:restriction base="xsd:string">
        <xsd:pattern value="([0-9])+,([0-9])+" />
    </xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="HexAreaBorder">
    <xsd:sequence minOccurs="1" maxOccurs="1">
        <xsd:element minOccurs="1" maxOccurs="1" name="type" type="xsd:string" />
        <xsd:element minOccurs="1" maxOccurs="1" name="name" type="xsd:string" />
        <xsd:element name="points" type="Point" minOccurs="1" maxOccurs="unbounded" />
    </xsd:sequence>
</xsd:complexType>

(1) Create adapter class that extends XmlAdapter.

package com.kjcode.hexgrid.jaxbadapter;
import java.awt.Point;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class PointAdapter extends XmlAdapter<String, Point> {

    @Override
    public Point unmarshal(String v) throws Exception {
        String[] coords = v.split(",");
        return new Point(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]));
    }

    @Override
    public String marshal(Point v) throws Exception {
        return String.format("%d,%d", v.x, v.y);
    }
}

(2) Create bindings file. The key is to add: jaxb:extensionBindingPrefixes="xjc" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    jaxb:extensionBindingPrefixes="xjc" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
    xmlns="hexmap" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    jaxb:version="2.1">

    <jaxb:bindings schemaLocation="hexmap.xsd">
        <jaxb:globalBindings>
            <xjc:javaType adapter="com.kjcode.hexgrid.jaxbadapter.PointAdapter" name="java.awt.Point" xmlType="Point" />
        </jaxb:globalBindings>
    </jaxb:bindings>
</jaxb:bindings>

(3) Configure pom.xml

<plugin>
    <groupId>com.sun.tools.xjc.maven2</groupId>
    <artifactId>maven-jaxb-plugin</artifactId>
    <version>1.1.1</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <generatePackage>com.kjcode.hexmap.api.logic.field.generated</generatePackage>
        <extension>true</extension>
    </configuration>
</plugin>

(4) JAXB will generate

public class HexAreaBorder {

    @XmlElement(required = true)
    protected String type;
    @XmlElement(required = true)
    protected String name;
    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(PointAdapter.class)
    protected List<Point> points = new LinkedList<Point>();
like image 58
Karol Król Avatar answered Oct 24 '22 03:10

Karol Król