JAXB cannot unmarshall - jaxb

I cannot unmarshall a generic Type, like this.
I do not like it solve with an adapter class.
Can someone help..
interface Car {
}
class Car1 implements Car {
}
class Car2 implements Car {
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType( name = "", propOrder = { "car1" } )
#XmlRootElement(name = "Element")
class Element{
#XMLELEMENT(type="Car1.class" name="Car")
private Car car1;
}

If the xml you try to unmarshall has different tag for describing car1 and car2, then it is possible by using #XmlElements annotation.
I am creating a small example below to show case it:
#XmlRootElement
public class SomeRoot {
#XmlElements({
#XmlElement(type = Car1.class, name = "car1"),
#XmlElement(type = Car2.class, name = "car2")
})
private List<Car> cars;
}
Then your interface:
public interface Car {
}
A base class with some common field:
#XmlAccessorType(XmlAccessType.FIELD)
public class BaseCar {
private String type;
}
And implementation for car1:
#XmlAccessorType(XmlAccessType.FIELD)
public class Car1 extends BaseCar implements Car {
private String car1SpecificField;
}
and car2:
#XmlAccessorType(XmlAccessType.FIELD)
public class Car2 extends BaseCar implements Car {
private String car2SpecificField;
}
This would unmarshal the xml below fine:
<someRoot>
<car1>
<type>some type</type>
<car1SpecificField>some value</car1SpecificField>
</car1>
<car2>
<type>other type</type>
<car2SpecificField>other value</car2SpecificField>
</car2>
</someRoot>
If your xml has the <car> instead of <car1> and <car2> you need to use an adapter or else jaxb doesn't know to which class to unmarshall.

Related

JAXB dynamic XML root class name

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.

XmlIDREF is not working

using JAXB 2.2.5b, and having
#XmlSeeAlso({BeanView.class, ViewField.class, ViewPanel.class, ViewTab.class, ElementRef.class})
#XmlRootElement
public abstract class ViewElement {
#XmlID
#XmlAttribute
public String getId() {
return id;
}
}
#XmlRootElement(name="ref")
public class ElementRef extends ViewElement{
private ViewElement target;
#XmlElement
#XmlIDREF
public ViewElement getTarget() {
return target;
}
public void setTarget(ViewElement target) {
this.target = target;
}
}
Many class extends the abstract ViewElement
#XmlRootElement
public class ViewField extends ViewElement{
}
#XmlRootElement
public class ViewPanel extends ViewElement{
}
in the XML the ElementRef target element is repeated instead of keeping only the id ref
<ref id="mvmCredit" >
<target xsi:type="viewField" valueExpr="" row="0" ... all ViewField attributes >
</target>
</ref>
and during the unmarchaling of the XML model the target is null (the restoration failed)
An upgrade to JAXB 2.2.7 fix the problem

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

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

Marshall object field as attribute

Here is what I have so far to marshall my POJO using JAXB :
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlElement
public Entity getEntity() {
return entity;
}
}
and
#XmlRootElement
public class Entity {
private String name;
private String comment;
public void setName(String name){
this.name = name;
}
#XmlAttribute
public String getName(){
return this.name;
}
public void setComment...
#XmlAttribute
public void getComment...
}
With that, I get something like this:
<local>
<entity name="" comment=""></entity>
</local>
However, I would prefer to have the name attribute as an attribute of the local:
<local entityName="" entityComment=""></local>
Is the XmlJavaTypeAdapter a good way to begin with?
Thanks,
Alex
There are a couple of different options to handle this use case:
Option #1 - XmlAdapter (Any JAXB implementation)
You could use an XmlAdapter for this use case. This will work as long as only one attribute value comes from the Entity object:
EntityAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class EntityAdapter extends XmlAdapter<String, Entity>{
#Override
public String marshal(Entity entity) throws Exception {
if(null == entity) {
return null;
}
return entity.getName();
}
#Override
public Entity unmarshal(String name) throws Exception {
Entity entity = new Entity();
entity.setName(name);
return entity;
}
}
Local
The XmlAdapter is linked with the field/property using the #XmlJavaTypeAdapter annotation:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlAttribute
#XmlJavaTypeAdapter(EntityAdapter.class)
public Entity getEntity() {
return entity;
}
}
For More Information
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
Option #2 - #XmlPath (EclipseLink JAXB (MOXy)
Alternatively if you are using EclipseLink JAXB (MOXy), the you could use the #XmlPath extension. This is useful with the Entity object corresponds to multiple XML attributes:
Local
Specifying the XPath "." indicated that the child contents will be written into the parent element
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#XmlRootElement
public class Local {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = entity;
}
#XmlPath(".")
public Entity getEntity() {
return entity;
}
}
Entity
public class Entity {
private String name;
private String comment;
public void setName(String name){
this.name = name;
}
#XmlAttribute(name="entityName")
public String getName(){
return this.name;
}
public void setComment(String comment){
this.comment = comment;
}
#XmlAttribute(name="entityComment")
public String getComment(){
return this.comment;
}
}
For More Information
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Resources