4spir
4spir

Reputation: 1184

JAXB polymorphic type object marshalling like Jackson

I use Jackson to create JSON for polymorphic objects using an annotation in the ancestor class:

// Include Java class name ("com.myempl.ImplClass") as JSON property "class"
@JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")

Using this annotation, the descendant classes will have the class property with the class name of the object. This class name will be used when Jackson converts back JSON object to the correct descendant.

When using JAX-B with

@XmlSeeAlso({Class1, Class2, ... ClassN })

annotation, you have to specify all the subclasses of the element if you want correct unmarshalling, or you should add all elements to the JAXBContext, when creating a new JAXBContext instance.

Is there a way to specify the object class name to JAX-B just like with Jackson? If there's no way, why?

Upvotes: 2

Views: 3378

Answers (1)

bdoughan
bdoughan

Reputation: 148977

I believe you are looking for the @XmlType annotation. Below is an example of how it can be used:

Java Model

Root

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Root {

    private List<AbstractClass> classes = new ArrayList<AbstractClass>();

    @XmlElement(name="class")
    public List<AbstractClass> getClasses() {
        return classes;
    }

}

AbstactClass

We will use the @XmlSeeAlso annotation to cause Class1and Class2 to be processed when the metadata is being figured out. Java does not provide a mechanism to reflectively access the subclasses for a class.

import javax.xml.bind.annotation.XmlSeeAlso;

@XmlSeeAlso({Class1.class, Class2.class})
public class AbstractClass {

}

Class1

By default each class has a class indicator name derived for the short class name.

public class Class1 extends AbstractClass {

}

Class2

We can use the @XmlType annotation to override the class indicator name.

import javax.xml.bind.annotation.XmlType;

@XmlType(name="class-two")
public class Class2 extends AbstractClass {

}

Demo Code

Demo

Below we will create an instance of Root and set instances of Class1 and Class2 on the classes property which is of the super type AbstractClass.

import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Root root = new Root();
        root.getClasses().add(new Class1());
        root.getClasses().add(new Class2());

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

Output

The value for the xsi:type for the instance of Class1 was derived from the class name, and the value for the instance of Class2 corresponds to the name we set on the @XmlType annotation.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <class xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="class1"/>
    <class xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="class-two"/>
</root>

Upvotes: 1

Related Questions