Showing posts with label Advanced. Show all posts
Showing posts with label Advanced. Show all posts

Thursday, 9 August 2012

JAXB Annotation - XmlAdapter, Map, Special Types Annotations

Code examples are available from Github in the JAXB-JSON-XML-Marshalling directory.

How to use XmlAdapter and @XmlJavaTypeAdapter to handle Map in JAXB?

In situations where JAXB cannot handle unknown types, the solution is to create an XmlAdapter and use the @XmlJavaTypeAdapter in the class referring to the unknown type to indicate JAXB how to deal with it. A Java map is a good example.

We are going to illustrate this with an example. Assuming an object containing a generic map:
@XmlRootElement
public class ObjectWithGenericMap<K, V> {

    private Map<K,V> map;

    @XmlElement
    @XmlJavaTypeAdapter(JaxbMapAdaptor.class)
    public Map<K,V> getMap() {
        return map;
    }

    public void setMap(Map<K,V> map) {
        this.map = map;
    }

}
We are going to need an adaptor:
public class JaxbMapAdaptor<K, V>
        extends XmlAdapter<JaxbMapToList<K, V>, Map<K, V>> {

    @Override
    public Map<K, V> unmarshal(JaxbMapToList<K, V> v)
            throws Exception {
        HashMap<K, V> result = new HashMap<K, V>();
        for (JaxbMapToListEntry<K, V> jme : v.getList()) {
            result.put(jme.getKey(), jme.getValue());
        }
        return result;
    }

    @Override
    public JaxbMapToList marshal(Map<K, V> v) throws Exception {
        JaxbMapToList<K, V> result = new JaxbMapToList<K, V>();
        for (Map.Entry<K, V> entry : v.entrySet()) {
            JaxbMapToListEntry<K, V> jme = new JaxbMapToListEntry<K, V>();
            jme.setKey(entry.getKey());
            jme.setValue(entry.getValue());
            result.getList().add(jme);
        }
        return result;
    }

}

Basically, the above adaptor converts a map into a list of entries (with key and value) when marshalling, and converts this list of entries back into a HashMap when unmarshalling.

The map-to-list and corresponding entries are:
public class JaxbMapToList <K, V> {

    private List<JaxbMapToListEntry<K, V>> list
        = new ArrayList<JaxbMapToListEntry<K, V>>();

    public JaxbMapToList() {}

    public JaxbMapToList(Map<K, V> map) {
        for (Map.Entry<K, V> e : map.entrySet()) {
            list.add(new JaxbMapToListEntry<K, V>(e));
        }
    }

    public List<JaxbMapToListEntry<K, V>> getList() {
        return list;
    }

    public void setList(List<JaxbMapToListEntry<K, V>> entry) {
        this.list = entry;
    }

}

public class JaxbMapToListEntry<K, V> {

    private K key;
    private V value;

    public JaxbMapToListEntry() { }

    public JaxbMapToListEntry(Map.Entry<K, V> e) {
        key = e.getKey();
        value = e.getValue();
    }

    @XmlElement
    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    @XmlElement
    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }

}
Assuming one instantiates the object with generic map with destination and transports:
@XmlRootElement
public class Destination {

    private String destination;

    public String getDestination() {
        return destination;
    }

    @XmlElement
    public void setDestination(String dest) {
        this.destination = dest;
    }

}

@XmlRootElement
public class Transport {

    private String transport;

    public String getTransport() {
        return transport;
    }

    @XmlElement
    public void setTransport(String transp) {
        this.transport = transp;
    }

}

public static void main(String[] args) throws JAXBException {

    JAXBContext jaxbContext = JAXBContext.newInstance(
        ObjectWithGenericMap.class,
        Destination.class, Transport.class);

    ObjectWithGenericMap<Destination,Transport> owgm
        = new ObjectWithGenericMap<Destination,Transport>();

    Map<Destination,Transport> map
        = new HashMap<Destination,Transport>();
    owgm.setMap(map);

    Destination d = new Destination();
    d.setDestination("Paris");

    Transport t = new Transport();
    t.setTransport("Plane");

    map.put(d,t);

    d = new Destination();
    d.setDestination("New-York");

    t = new Transport();
    t.setTransport("Boat");

    map.put(d,t);

    ObjectWithGenericMap<Destination,Transport> retr
        = marshallingUnmarshalling(jaxbContext, owgm);

    for (Entry<Destination, Transport> e : retr.getMap().entrySet() ){
        Destination retrd = e.getKey();
        System.out.print(retrd.getDestination());
        Transport retrt = e.getValue();
        System.out.println(" " + retrt.getTransport());
    }

}
The generated XML and verification is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithGenericMap>
    <map>
        <list>
            <key xsi:type="destination" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <destination>New-York</destination>
            </key>
            <value xsi:type="transport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <transport>Boat</transport>
            </value>
        </list>
        <list>
            <key xsi:type="destination" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <destination>Paris</destination>
            </key>
            <value xsi:type="transport" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <transport>Plane</transport>
            </value>
        </list>
    </map>
</objectWithGenericMap>
Paris Plane
New-York Boat

How to use: @XmlList, @XmlEnum, @XmlAttribute, @XmlValue, @XmlMimeType, @XmlInlineBinaryData?


To annotate an enum:
@XmlEnum
public enum MyEnum {

    @XmlEnumValue("v1")
    VAL_1,

    @XmlEnumValue("v2")
    VAL_2;

}
To specify the xml tag value and attributes:
@XmlAccessorType(XmlAccessType.FIELD)
public class SpecialItem {

    @XmlValue
    private String val;

    @XmlAttribute
    private String attribute1;

    @XmlAttribute
    private String attribute2;

    // Setter & Getters...

}
For remaining annotations and usage of the above:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class MultipleTypes {

    @XmlElement
    private MyEnum myEnum;

    @XmlElement
    @XmlList
    private List<String> data;

    @XmlElement
    private List<SpecialItem> specialItems;

    @XmlMimeType("image/jpeg")
    private Image image;

    @XmlInlineBinaryData
    private byte[] byteArray;

    // Setter & Getters...

}
The generated XML and verification is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<multipleTypes>
    <myEnum>v2</myEnum>
    <data>tre pml xng</data>
    <specialItems attribute2="ffff" attribute1="aaaa">pppp</specialItems>
    <specialItems attribute2="pqgd" attribute1="mer">xxw</specialItems>
    <image>/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAALABADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwCxBe6S1ha3ptg4MEYF29lGSXKA5JIznHvmuX1UaCmhapbwxW2023mxkWUSyedsbjPXaOTkdPX+9wieM/EEOlrpkeoEWS7SIfKQj5Rgfw88AflUb+JdVl0O7tHniMMkqbh9njDcq4Pzbcjj0PrXppRo3SuTVqVKrV7aH//Z</image>
    <byteArray>AQL9/A==</byteArray>
</multipleTypes>
pppp aaaa ffff
xxw mer pqgd
VAL_2
tre, pml, xng, 
1 2 -3 -4
Image is available: true

Remark

We have not covered all JAXB annotations, especially those related to XML schemas, but this tutorial should be enough to get one started and accomplish most objectives with JXTA.

JAXB to XMLJAXB to JSONJAXB Annotations Tutorial Table Of Content

Tuesday, 7 August 2012

JAXB Annotation - Can't Handle Interfaces

JAXB Can't Handle Interfaces in Generics

If you have a class with a List<Vehicle> and Vehicle is an interface, JAXB will throw an Exception at runtime:
my.package.MyInterface is an interface,
    and JAXB can't handle interfaces
The solution is to maket the interface an abstract class. For example:
public abstract class Vehicle {

    public abstract String getType();

}

@XmlRootElement
public class Bus extends Vehicle {

    private String type;

    public Bus() { };

    public Bus(String type) {
        this.type = type;
    }

    @XmlElement
    @Override
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

}

@XmlRootElement
public class Car extends Vehicle {

    private String type;

    public Car() {};

    public Car(String type) {
        this.type = type;
    }
 
    @XmlElement
    @Override
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

}

@XmlRootElement
public class ObjectWithListOfVehicles {

    private List<Vehicle> list;

    @XmlElementWrapper(name="MyVehicleList")
    @XmlElement
    public List<Vehicle> getList() {
        return list;
    }

    public void setList(List<Vehicle> list) {
        this.list = list;
    }

}
Assuming the following JAXB context:
public static void interfaceExamples() throws JAXBException {

    List<Vehicle> l = new ArrayList<Vehicle>();
    l.add(new Bus("Large bus"));
    l.add(new Bus("Small bus"));
    l.add(new Car("Ferrari"));

    // Object with generic list
    ObjectWithListOfVehicles owgl
        = new ObjectWithListOfVehicles();
    owgl.setList(l);

    JAXBContext jc = JAXBContext.newInstance(
        Bus.class, Car.class,
        ObjectWithListOfVehicles.class);

    ObjectWithListOfVehicles retr
        = marshallUnmarshall(owgl, jc);

    for (Vehicle s : retr.getList()) {
        System.out.println(
            s.getClass().getSimpleName()
            + " - " + s.getType());
    } System.out.println(" ");

}

public static <O> O marshallUnmarshall(O o, JAXBContext jc)
        throws JAXBException {

    // Creating a Marshaller
    Marshaller jaxbMarshaller = jc.createMarshaller();
    jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    StringWriter result = new StringWriter();
    jaxbMarshaller.marshal(o, result);

    // Printing XML
    String xml = result.toString();
    System.out.println(xml);

    // Creating an Unmarshaller
    Unmarshaller jaxbUnmarshaller = jc.createUnmarshaller();
    StringReader sr = new StringReader(xml);

    O retr = (O) jaxbUnmarshaller.unmarshal(sr);

    return retr;

}
The generated XML and verification is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithListOfVehicles>
    <MyVehicleList>
        <list xsi:type="bus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Large bus</type>
        </list>
        <list xsi:type="bus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Small bus</type>
        </list>
        <list xsi:type="car" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>Ferrari</type>
        </list>
    </MyVehicleList>
</objectWithListOfVehicles>

Bus - Large bus
Bus - Small bus
Car - Ferrari

Notice that JAXB is capable of recreating both Bus and Car instances.

Next

JAXB to XMLJAXB to JSONJAXB Annotations Tutorial Table Of Content

JAXB Annotation - JAXB Context, Generics

Understanding JAXB Context

In order to marshall and unmarshall JAXB annotated classes, one needs to create a JAXBContext. The instantiator method takes in a list of JAXB annotated Class.

By default, JAXB will include statically referenced classes in the list and inherited classes, but not transient or inheriting classes. The code examples are available from Github in the JAXB-JSON-XML-Marshalling directory.

Assuming:
@XmlRootElement
public class StaticallyReferenced {

    private String data;

    @XmlElement
    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
    
}

@XmlRootElement
public class StaticallyReferencedButTransient {
    
    private String data;

    @XmlElement
    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
    
}

@XmlRootElement
public class SomeA {
    
    private StaticallyReferenced sr;
    private StaticallyReferencedButTransient srbt;

    @XmlElement
    public StaticallyReferenced getSr() {
        return sr;
    }

    public void setSr(StaticallyReferenced sr) {
        this.sr = sr;
    }

    @XmlTransient
    public StaticallyReferencedButTransient getSrbt() {
        return srbt;
    }

    public void setSrbt(
            StaticallyReferencedButTransient srbt) {
        this.srbt = srbt;
    }

}

@XmlRootElement
public class InheritSomeA extends SomeA {
    
    private String moreData;

    @XmlElement
    public String getMoreData() {
        return moreData;
    }

    public void setMoreData(String moreData) {
        this.moreData = moreData;
    }
    
}
If one creates the following JAXB context:
JAXBContext jaxbContext =
    JAXBContext.newInstance(SomeA.class);
JAXB will only be able to process SomeA and StaticallyReferenced, not StaticallyReferencedButTransient or InheritSomeA.

How to use JAXB with Generics such as List, Set, etc...?

Assuming a simple example, where an object contains a List<String>:
@XmlRootElement
public class ObjectWithList {

    private List<String> list;

    @XmlElementWrapper(name="MyList")
    @XmlElement
    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

}
If the JAXB context is built as following (before a round trip):
public static void simpleExample() throws JAXBException {

    List<String> l = new ArrayList<String>();
    l.add("Somewhere");
    l.add("This and that");
    l.add("Something");

    // Object with list
    ObjectWithList owl = new ObjectWithList();
    owl.setList(l);

    JAXBContext jc = JAXBContext.newInstance(ObjectWithList.class);
    ObjectWithList retr = marshallUnmarshall(owl, jc);

    for (String s : retr.getList()) {
        System.out.println(s);
    } System.out.println(" ");

}
The generated XML with verification is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithList>
    <MyList>
        <list>Somewhere</list>
        <list>This and that</list>
        <list>Something</list>
    </MyList>
</objectWithList>

Somewhere
This and that
Something
A more sophisticated example with a generic list:
@XmlRootElement
public class ObjectWithGenericList<T> {

    private List<T> myList;

    @XmlElementWrapper(name="MyGenericList")
    @XmlElement
    public List<T> getList() {
        return myList;
    }

    public void setList(List<T> list) {
        this.myList = list;
    }

}
used with the following JAXB context and verification:
public static void genericListExample()
        throws JAXBException {

    List<Car> l = new ArrayList<Car>();
    l.add(new Car("red car"));
    l.add(new Car("blue car"));
    l.add(new Car("green car"));

    // Object with generic list
    ObjectWithGenericList<Car> owgl
        = new ObjectWithGenericList<Car>();
    owgl.setList(l);

    JAXBContext jc = JAXBContext.newInstance(
        ObjectWithGenericList.class, Car.class);
    ObjectWithGenericList<Car> retr
        = marshallUnmarshall(owgl, jc);

    for (Car s : retr.getList()) {
        System.out.println(
        s.getClass().getSimpleName() + " - " + s.getType());
    } System.out.println(" ");

}
generates the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objectWithGenericList>
    <MyGenericList>
        <list xsi:type="car" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>red car</type>
        </list>
        <list xsi:type="car" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>blue car</type>
        </list>
        <list xsi:type="car" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <type>green car</type>
        </list>
    </MyGenericList>
</objectWithGenericList>

Car - red car
Car - blue car
Car - green car
Notice that JAXB is capable of recreating Car instances.

Next

JAXB to XMLJAXB to JSONJAXB Annotations Tutorial Table Of Content