JAXB dynamic XML root class name - jaxb

I have my User class in java. When I want to unmarshal it, I get xml with <UserIn> root element, and when I want to marshal it I should do <UserOut> to be XML root element. If I provide #XmlRootElement("UserIn") it is not dynamic and it is always UserIn root. Is there any way to do dynamic root element on class? thanks.

You could create two classes that extend your User class, and then use the specific child class based on if you are marshalling on unmarshalling.
For example, for a class User:
public class User {
#XmlElement
private String value;
public User() { }
public User(String value) {
this.value = value;
}
}
You can have UserIn:
#XmlRootElement(name = "UserIn")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserIn extends User {
public UserIn() { }
public UserIn(String value) {
super(value);
}
}
and UserOut:
#XmlRootElement(name = "UserOut")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserOut extends User {
public UserOut() { }
public UserOut(String value) {
super(value);
}
}
Provide the appropriate class where you need, and you will get it working with the input or output you wish.

Related

How to override JAXB #XMLAccessorType(XMLAccessType.FIELD) specified at a Class level with #XMLElement on a getter method for a property?

In the example code below, Employee class has been specified with JAXB field level access type. For the property dept, however, the access type has been specified at getter method level with #XMLElement annotation.
During marshalling of Organization class, the following exception is thrown -
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "dept"
this problem is related to the following location:
at public java.lang.String com.playground.jaxb.Employee.getDept()
this problem is related to the following location:
at private java.lang.String com.playground.jaxb.Employee.dept
Can you help me understand why this overriding of JAXB accessor type is not working please? Also any solution would be highly appreciated.
Example
Root Element Class
package com.playground.jaxb;
#XMLRootElement(name="organization")
public class Organization {
#XmlElementWrapper(name = "employees")
#XmlElement(name = "employee")
private Set<Employee> employees;
public Organization{}
// Remainder omitted...
}
Employee Class
package com.playground.jaxb;
#XMLAccessorType(XMLAccessType.FIELD)
public class Employee {
private String name;
private String dept;
#XMLElement(name="department")
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public Employee {}
// Remainder omitted...
}
You can re-name getter/setter pair, e.g. getDept() -> getDepartment()
private String dept;
#XmlElement(name="department")
public String getDeptartment() {
return dept;
}
public void setDeptartment(String dept) {
this.dept = dept;
}
but in this case you will have duplicate in XML
<dept>my_dept</dept>
<department>my_dept</department>
Or you can annotate field dept with #XmlTransient annotation, if you want to change access type it.
#XmlTransient
private String dept;
#XmlElement(name="department")
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
In this case, dept field will be ignored and getter/setter pair will be used instead

JAXB and inheritance

I am trying to read a JSON file like:
{
"a": "abc",
"data" : {
"type" : 1,
...
}
}
where the ... part is replaceable based on the type like:
{
"a": "abc",
"data" : {
"type" : 1,
"b" : "bcd"
}
}
or:
{
"a": "abc",
"data" : {
"type" : 2,
"c" : "cde",
"d" : "def",
}
}
For the life of me I cannot figure out the proper JAXB annotations/classes to use to make this happen.
I don't have an issue moving the type variable outside of the data block if needed.
I'm using Glassfish 3.1.2.2.
Edit:
Based on the code provided by Perception I did a quick attempt... doesn't work in glassfish though:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes(
{
#JsonSubTypes.Type(value = DataSubA.class, name = "1"),
#JsonSubTypes.Type(value = DataSubB.class, name = "2")
})
#XmlRootElement
public abstract class Data implements Serializable
{
private static final long serialVersionUID = 1L;
public Data()
{
super();
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class DataSubA
extends Data
{
private static final long serialVersionUID = 1L;
#XmlElement
private BigDecimal expenditure;
public DataSubA() {
super();
}
public DataSubA(final BigDecimal expenditure) {
super();
this.expenditure = expenditure;
}
#Override
public String toString() {
return String.format("%s[expenditure = %s]\n",
getClass().getSimpleName(), getExpenditure());
}
public BigDecimal getExpenditure() {
return expenditure;
}
public void setExpenditure(BigDecimal expenditure) {
this.expenditure = expenditure;
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class DataSubB
extends Data
{
private static final long serialVersionUID = 1L;
#XmlElement
private String name;
#XmlElement
private Integer age;
public DataSubB()
{
super();
}
public DataSubB(final String name, final Integer age)
{
super();
this.name = name;
this.age = age;
}
#Override
public String toString()
{
return String.format("%s[name = %s, age = %s]\n",
getClass().getSimpleName(), getName(), getAge());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class DataWrapper
{
#XmlElement
private Data data;
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
}
And a simple POST that takes it in:
#Stateless
#Path("x")
public class Endpoint
{
#POST
#Consumes(
{
MediaType.APPLICATION_JSON,
})
#Produces(
{
MediaType.APPLICATION_JSON,
})
public String foo(final DataWrapper wrapper)
{
return ("yay");
}
}
When I pass in JSON like:
{
"data" :
{
"type" : 1,
"expenditure" : 1
}
}
I get a message like:
Can not construct instance of Data, problem: abstract types can only be instantiated with additional type information
at [Source: org.apache.catalina.connector.CoyoteInputStream#28b92ec1; line: 2, column: 5] (through reference chain: DataWrapper["data"])
On the DataClass add an #XmlSeeAlso annotation that specifies all of the subclasses:
#XmlRootElement
#XmlSeeAlso({DataSubA.class, DataSubB.class})
public abstract class Data implements Serializable {
Then on each of the subclasses use the #XmlType annotation to specify the type name.
#XmlType(name="1")
public class DataSubA extends Data {
UPDATE
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The JAXB (JSR-222) specification doesn't cover JSON-binding. There are different ways JAX-RS allows you to specify JSON mapping via JAXB annotations:
A JAXB implementation plus a library like Jettison that converts StAX events to JSON (see: http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html)
By leveraging a JAXB impl that offers a JSON-binding (see: http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html)
Leveraging a JSON-binding tool that offers support for some JAXB metadata (i.e Jackson).
Since your model doesn't seem to be reacting as expected to the annotations I'm guessing you are using scenario 3. Below I will demonstrate the solution as if you were using scenario 2.
DataWrapper
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DataWrapper {
private String a;
private Data data;
}
Data
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({DataSubA.class, DataSubB.class})
public class Data {
}
DataSubA
import javax.xml.bind.annotation.XmlType;
#XmlType(name="1")
public class DataSubA extends Data {
private String b;
}
DataSubB
import javax.xml.bind.annotation.XmlType;
#XmlType(name="2")
public class DataSubB extends Data {
private String c;
private String d;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {DataWrapper.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum16429717/input.json");
DataWrapper dataWrapper = unmarshaller.unmarshal(json, DataWrapper.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(dataWrapper, System.out);
}
}
input.json/Output
MOXy can read in the numeric value 2 as the inheritance indicator, but currently it will always write it out as "2". I have opened the following enhancement request to address this issue: http://bugs.eclipse.org/407528.
{
"a" : "abc",
"data" : {
"type" : "2",
"c" : "cde",
"d" : "def"
}
}
For More Information
The following link will help you use MOXy in a JAX-RS implementation.
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html

Creating xml from JAXB when one value is retrieved using a factory method

I have a UUID class where I get the uuid from a static factory method like
UUIDGenerator.getInstance().getUuid();
I have another class which has these UUids as a list.
class Artifact
{
Uuid uuid;
setUuid(Uuid uuid)
{
this.uuid = uuid;
}
Uuid getUuid()
{
return this.uuid;
}
}
Class ArtifactData
{
private List<Artifact> artifacts;
//setter for list
// getter for list
}
I want the xml to be created as
<ArtifactData>
<AssociatedArtifactList>
<ArtifactUuid>#some value<ArtifactUuid>
</AssociatedArtifactList>
</ArtifactData>
How do I create this xml out of Jaxb annotations. It complains on saying there isn't a public constructor for Uuid because it is constructed out of a factory method.
EDIT: The Uuid and Uuid generator cannot be modified. They are in a JAR. This is what I have tried so far
public class Artifact
{
#XmlJavaTypeAdapter(ArtifactUuidAdapter.class)
private Uuid uuid;
public Uuid getUuid()
{
return uuid;
}
public void setUuid(Uuid uuid)
{
this.uuid = uuid;
}
}
#XmlRootElement
public class ArtifactData
{
private List<Artifact> associatedArtifactList;
public List<Artifact> getArtifacts()
{
return associatedArtifactList;
}
#XmlElementWrapper(name="associatedArtifactList")
#XmlElement(name = "artifactUuid")
public void setArtifacts(List<Artifact> artifacts)
{
this.associatedArtifactList = artifacts;
}
}
public class ArtifactUuidAdapter extends XmlAdapter<Uuid, String>
{
#Override
public Uuid marshal(String uuid) throws Exception
{
return Uuid.getInstance(uuid);
}
#Override
public String unmarshal(Uuid uuid) throws Exception
{
return uuid.getData();
}
}
I still get an error called no arg default constructor is missing.
The #XmlType annotation allows you to configure a factory class and method. The factory class described in your questions doesn't quite meet the API requirements but you could easily create an adapter for it.
UUIDGeneratorAdapter
The class below would adapt your factory class to something that a JAXB (JSR-222) implementation could leverage.
public class UUIDGeneratorAdapter {
public static Uuid getUuid() {
return UUIDGenerator.getInstance().getUuid();
}
}
Uuid
Below is an example of how you configure the factory class through the #XmlType annotation.
import javax.xml.bind.annotation.XmlType;
#XmlType(factoryClass=UUIDGeneratorWrapper.class, factoryMethod="getUuid")
public class Uuid {
// ...
}
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html

Use XmlIDRef for reference to abstract class

First of all a small example. The class ReferencingEntity holds a reference to the abstract class AbstractEntity. There are two implementations fo this class:
#XmlRootElement
public abstract class AbstractEntity {
#XmlID
private String id;
}
#XmlRootElement
public class EntityImpl1 extends AbstractEntity {
}
#XmlRootElement
public class EntityImpl2 extends AbstractEntity {
}
#XmlRootElement
public class ReferencingEntity {
#XmlIDREF
private AbstractEntity entity;
}
There is no problem marshalling an instance of ReferencingEntity (except that the concrete type is not present in xml), but when trying to unmarshal the xml representation, the descriptor is missing to determine the concrete implementation.
Currently I'm using an XmlAdapter to set all non-id fields null, but it would be better to use #XmlID if possible. Any ideas?
UPDATE:
I'm using RESTEasy in JBoss 6.1.0.Final and the provider creates the context as follows:
ContextResolver<JAXBContextFinder> resolver = providers.getContextResolver(JAXBContextFinder.class, mediaType);
JAXBContextFinder finder = resolver.getContext(type);
if (finder == null)
{
if (reader) throw new JAXBUnmarshalException("Could not find JAXBContextFinder for media type: " + mediaType);
else throw new JAXBMarshalException("Could not find JAXBContextFinder for media type: " + mediaType);
}
JAXBContext context = finder.findCachedContext(type, mediaType, annotations);
Below is my initial answer to your question. I imagine it will evolve as I better understand your use case.
ABOUT #XmlID/#XmlIDREF
Every instance referenced from a field/property annotated with #XmlIDREF also needs to be referenced via containment. I'll use the class below in this example.
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private AbstractEntity abstractEntity;
private ReferencingEntity referencingEntity;
public AbstractEntity getAbstractEntity() {
return abstractEntity;
}
public void setAbstractEntity(AbstractEntity abstractEntity) {
this.abstractEntity = abstractEntity;
}
public ReferencingEntity getReferencingEntity() {
return referencingEntity;
}
public void setReferencingEntity(ReferencingEntity referencingEntity) {
this.referencingEntity = referencingEntity;
}
}
REGARDING INHERITANCE
JAXB (JSR-222) implementations can't automatically discover subclasses, so you will need to be sure that the JAXBContext is aware of them. One way to accomplish this is to use the #XmlSeeAlso annotation on the parent class to point at the child classes.
import javax.xml.bind.annotation.*;
#XmlSeeAlso({EntityImpl1.class, EntityImpl2.class})
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractEntity {
#XmlID
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
DEMO CODE
Demo
package forum12111815;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum12111815/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
System.out.println(root.getAbstractEntity().getClass());
System.out.println(root.getAbstractEntity() == root.getReferencingEntity().getEntity());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<abstractEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="entityImpl2">
<id>123</id>
</abstractEntity>
<referencingEntity>
<entity>123</entity>
</referencingEntity>
</root>
Output
class forum12111815.EntityImpl2
true
<?xml version="1.0" encoding="UTF-8"?>
<root>
<abstractEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="entityImpl2">
<id>123</id>
</abstractEntity>
<referencingEntity>
<entity>123</entity>
</referencingEntity>
</root>
FOR MORE INFORMATION
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

JAXB/MOXy: How to partially unmarshall, passing the descendants of a given node to a closed/proprietary class?

I'm starting with some Java classes that I would like to be able to unmarshall from XML--I'm determining the schema as I go. I would like to use XML similar to the following:
<Person fname="John" lname="Doe">
<bio><foo xmlns="http://proprietary.foo">Blah <bar>blah</bar> blah</foo></bio>
</Person>
I'm hoping to annontate my Java classes similar to the following:
public class Person {
#XmlAttribute
public String fname;
#XmlAttribute
public String lname;
#XmlElement
public ProprietaryFoo bio;
}
I'd like to pass the <foo xmlns="http://proprietary.foo"> element and it's descendants to a compiled factory class which works like this:
FooFactory.getFooFromDomNode(myFooElement) // Returns a private ProprietaryFooImpl as an instance of the public ProprietaryFoo Interface
It seems like I need to create a DomHandler for ProprietaryFoo but I'm not quite able to figure it out (I was getting “com.xyz.ProprietaryFooImpl nor any of its super class is known to this context.") I'm also interested in XmlJavaTypeAdapter I can't figure out how to receive the ValueType as an Element.
Ended up using both an XmlAdapter and a DomHandler along with a simple Wrapper class.
public class FooWrapper {
#XmlAnyElement(FooDomHandler.class)
public ProprietaryFoo foo;
}
public class FooXmlAdapter extends XmlAdapter<FooWrapper, ProprietaryFoo> {
#Override
public ProprietaryFoo unmarshal(FooWrapper w) throws Exception {
return w.foo;
}
#Override
public FooWrapper marshal(ProprietaryFoo f) throws Exception {
FooWrapper fooWrapper = new FooWrapper();
fooWrapper.foo = f;
return fooWrapper;
}
}
/* The vendor also provides a ProprietaryFooResult class that extends SAXResult */
public class FooDomHandler implements DomHandler<ProprietaryFoo, ProprietaryFooResult> {
#Override
public ProprietaryFooResult createUnmarshaller(ValidationEventHandler validationEventHandler) {
return new ProprietaryFooResult();
}
#Override
public ProprietaryFoo getElement(ProprietaryFooResult r) {
return r.getProprietaryFoo();
}
#Override
public Source marshal(ProprietaryFoo f, ValidationEventHandler validationEventHandler) {
return f.asSaxSource();
}
}
For whatever reason, this didn't work with the standard classes from the com.sun namespace but MOXy handles it well.

Resources