Order xml superclass elements in Java serialization - java

I have two classes ParentClass and ChildClass in JAVA using JAXB.
ChildClass extends ParentClass.
When I serialize an object of ChildClass, in the resulting XML, ParentClass properties appear first, I would like to have ChildClass properties first and then ParentClass properties.
Is this possible?
Thank you

The reason JAXB does this is to match inheritance in XML schema. However, you could do something like the following:
Mark the parent #XmlTransient
Set the propOrder on the child class
Parent
import javax.xml.bind.annotation.XmlTransient;
#XmlTransient
public abstract class Parent {
private String parentProp;
public String getParentProp() {
return parentProp;
}
public void setParentProp(String parentProp) {
this.parentProp = parentProp;
}
}
Child
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlType(propOrder={"childProp", "parentProp"})
public class Child extends Parent {
private String childProp;
public String getChildProp() {
return childProp;
}
public void setChildProp(String childProp) {
this.childProp = childProp;
}
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Child.class);
Child child = new Child();
child.setParentProp("parent-value");
child.setChildProp("child-value");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(child, System.out);
}
}
Output
<child>
<childProp>child-value</childProp>
<parentProp>parent-value</parentProp>
</child>

Related

JAXB - XmlElement with multiple names and types

I have the following class hierarchy:
#XmlRootElement
public abstract class Animal{}
#XmlRootElement
public class Dog extends Animal{}
#XmlRootElement
public class Cat extends Animal{}
#XmlRootElement
public class Lion extends Animal{}
and a class which has an attribute named animal:
#XmlRootElement
public class Owner{
private Animal animal;
}
I would like to allow different XML Schemas as follows and bind the Animal Type in the schema to animal object in Owner class
<Owner>
<Dog></Dog>
</Owner>
<Owner>
<Cat></Cat>
</Owner>
<Owner>
<Lion></Lion>
</Owner>
The solutions that I have found use XmlElements which can take multiple XmlElement fields and creates a collection. However, in my case I don't need a collection but a single attribute.
Does JAXB allow any XmlElement multiple naming convention for this problem?
Is there any other annotation which could solve this problem?
Note: I have looked at multiple answers to similar questions in stackoverflow and around but almost all of them create a collection instead of a single object. The closest answer I have found is this : #XmlElement with multiple names
Edit : I think this solution might work. Have to test it out
I got it to work using the #XmlElements annotation, as follows:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws JAXBException {
String xml = "<owner><dog></dog></owner>";
JAXBContext jaxbContext = JAXBContext.newInstance(Owner.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Owner owner = (Owner) jaxbUnmarshaller.unmarshal(
new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
System.out.println(owner.getAnimal().getClass());
}
}
abstract class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Lion extends Animal {}
#XmlRootElement
class Owner {
#XmlElements({
#XmlElement(name = "dog", type = Dog.class),
#XmlElement(name = "cat", type = Cat.class),
#XmlElement(name = "lion", type = Lion.class)
})
private Animal animal;
public Animal getAnimal() {
return animal;
}
}
Using the default JAXB implementation that ships with the Oracle Java 8 SDK, this prints out:
class Dog
I want to offer an alternative solution. The previous solution is fine - but you'll notice that the #XmlElements annotation creates strong dependencies between the Owner.class - and the concrete implementations of your animals (Dog.class, Cat.class, Lion.class) This can be source of frustration - causing you to recompile your Owner class every time you add a new implementation of Animal. (We have a microservice architecture and continuous delivery - and couplings of this sort were not ideal for our build process...)
Instead - consider this decoupled solution. New animal implementations can be created and used - without recompiling the Owner class - satisfying the Open Closed principle.
Start with an Owner class that defines an Abstract Animal element.
package com.bjornloftis.domain;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name = "owner")
public class Owner {
#XmlElement(name = "animal")
#XmlJavaTypeAdapter(AnimalXmlAdapter.class)
private Animal animal;
public Owner() {
}
public Owner(Animal animal) {
this.animal = animal;
}
public Animal getAnimal() {
return animal;
}
}
Now you'll need an abstract class and a interface. This will be important for marshalling and unmarshalling.
package com.bjornloftis.domain;
import javax.xml.bind.annotation.XmlTransient;
#XmlTransient
public abstract class Animal implements AnimalType{
}
The AnimalType interface defines a method that ensures at runtime that JaxB can determine which implementation should be used to unmarshall an XML document. It is used by our XmlAdapter - which you will see shortly. Otherwise - JAXB would not be able to derive the implementing class at runtime.
package com.bjornloftis.domain;
import javax.xml.bind.annotation.XmlAttribute;
public interface AnimalType {
#XmlAttribute(name = "type")
String getAnimalType();
}
Now - you'll have a wrapper for your animal - and the animal implementation itself. This can be compiled separately from the owner. Not coupled at compile time.
package com.bjornloftis.domain;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "animal")
#XmlAccessorType(XmlAccessType.FIELD)
public class DogWrapper extends Animal {
private Dog dog;
public DogWrapper(){
}
public DogWrapper(Dog dog) {
dog = dog;
}
public Dog getDog() {
return dog;
}
public void setError(Dog dog) {
this.dog = dog;
}
#Override
#XmlAttribute(name = "type")
public String getAnimalType(){
return "dog";
}
}
And the animal itself:
package com.bjornloftis.domain;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "dog")
public class Dog {
#XmlElement(name = "name")
private String name;
public Dog() {
}
}
Finally - to tie it all together - you'll need to implement the XmlAdapter - which will facilitate marshalling and unmarshalling.
package com.bjornloftis.domain;
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.w3c.dom.Node;
import com.bjornloftis.registry.PropertyRegistryFactory;
public class AnimalXmlAdapter extends XmlAdapter<Object, Animal> {
#Override
public Animal unmarshal(Object elementNSImpl) throws Exception {
Node node = (Node)elementNSImpl;
String simplePayloadType = node.getAttributes().getNamedItem("type").getNodeValue();
Class<?> clazz = PropertyRegistryFactory.getInstance().findClassByPropertyName(simplePayloadType);
JAXBContext jc = JAXBContext.newInstance(clazz);
Binder<Node> binder = jc.createBinder();
JAXBElement<?> jaxBElement = binder.unmarshal(node, clazz);
return (Animal)jaxBElement.getValue();
}
#Override
public Animal marshal(Animal animal) throws Exception {
return animal;
}
}
Finally - we need to associate the type "dog" with the wrapper class DogWrapper.class This is done with a registry that we initialize at runtime in the code that will marshall or unmarshall dogs.
package com.bjornloftis.registry;
import com.bjornloftis.registry.PropertyRegistry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PropertyRegistryFactory {
private static final Map<String, Class<?>> DEFAULT_REGISTRY = new ConcurrentHashMap();
public PropertyRegistryFactory() {
}
public static final PropertyRegistry getInstance() {
return new PropertyRegistry(DEFAULT_REGISTRY);
}
public static final void setDefaultRegistry(Map<String, Class<?>> defaultRegistry) {
DEFAULT_REGISTRY.putAll(defaultRegistry);
}
}
This is all extracted from our production code - and somewhat sanitized to remove proprietary IP.
If it is hard to follow - let me know in a comment - and I'll bundle it all up into a working project on github.
Again, understood to be a much more complicated solution - but necessary to avoid coupling our code. An additional benefit is this also works with Jackson's libraries pretty seamlessly for JSON. For JSON marshalling and unmarshalling - we have a similar set of annotations using the TypeIdResolver - which provides a function analogous to the XmlAdapter for JAXB.
The end result is that you can marshall and unmarshall the following - but without the nasty compile time coupling that #XmlElements introduces:
<owner>
<animal type="dog">
<dog>
<name>FIDO</name>
</dog>
</animal>
</owner>

JAXB un-marshaling return null

I have a class with the following structure
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
Child.class
})
public abstract class Parent implements Serializable{}
And
public class Child extends Parent implements Serializable{
private String attribute;
private List<String> values = new ArrayList<String>() ;
}
So while marshaling a child object it is saved successfully at the database as :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Child >
<attribute>age</attribute>
<values>1</values>
<values>2</values>
</Child >
The problem is while un-marshaling that object , The unmarshal function return null.
JAXB.unmarshal(reader, Parent.class)
Could you please advise what is the problem , and how to solve it ?
Thanks in advance
When unmarshalling, you must:
Provide as root of your XML an element marked with #XMLRootElement.
Define your classes as fully compliant Java Beans (get/set methods over attributes).
Here is the code that worked for me:
Parent.java
package dummy;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlRootElement
#XmlSeeAlso({
Child.class
})
public abstract class Parent implements Serializable{}
Child (with a small main method marshalling and unmarshalling the same payload)
package dummy;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Child extends Parent implements Serializable
{
private String attribute;
private List<String> values = new ArrayList<String>();
/**
* #return the attribute
*/
public String getAttribute()
{
return attribute;
}
/**
* #param attribute
* the attribute to set
*/
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
/**
* #return the values
*/
public List<String> getValues()
{
return values;
}
/**
* #param values
* the values to set
*/
public void setValues(List<String> values)
{
this.values = values;
}
public static void main(String[] args) throws JAXBException
{
JAXBContext context = JAXBContext.newInstance(Parent.class);
Child child = new Child();
child.setAttribute("dummy");
child.setValues(Arrays.asList("value1", "value2"));
StringWriter writer = new StringWriter();
context.createMarshaller().marshal(child, writer);
System.out.println(writer.getBuffer().toString());
Child unmarshalledChild = (Child) context.createUnmarshaller().unmarshal(new ByteArrayInputStream(writer.getBuffer().toString().getBytes()));
System.out.println("attribute: " + unmarshalledChild.attribute);
System.out.println("values: " + unmarshalledChild.values);
}
}
And the successful output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><child> <attribute>dummy</attribute><values>value1</values><values>value2</values></child>
attribute: dummy
values: [value1, value2]

java jaxb simple parsing is requiring #XmlAccessorType(XmlAccessType.FIELD) annotation

I am trying to parse an xml to java objects, I've read and implemented the following tutorial:
http://www.vogella.com/articles/JAXB/article.html (works perfectly)
But when I create my own clases (similar to those in the tutorial)
I get: Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "clienteList"
Unless I use #XmlAccessorType(XmlAccessType.FIELD) on class Clientes but in the tutorial is not being used.
Any ideas ?
(It works fine with the #XmlAccessorType(XmlAccessType.FIELD) annotation but I want to know why is it being required with my classes while it is not for the classes in the tutorial)
Thank you in advance for any information.
Class Cliente
package mx.com.findep.crediseguros.dto.servicios.finsol;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "cliente")
public class Cliente {
private String numeroPersona;
#XmlElement(name = "numeroPersona")
public String getNumeroPersona() {
return numeroPersona;
}
public void setNumeroPersona(String numeroPersona) {
this.numeroPersona = numeroPersona;
}
}
Class Clientes
package mx.com.findep.crediseguros.dto.servicios.finsol;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "clientes")
//#XmlAccessorType(XmlAccessType.FIELD) //without this line it fails
public class Clientes {
// XmLElementWrapper generates a wrapper element around XML representation
#XmlElementWrapper(name = "clienteList")
// XmlElement sets the name of the entities
#XmlElement(name = "cliente")
private ArrayList<Cliente> clienteList;
public void setClienteList(ArrayList<Cliente> clienteList) {
this.clienteList = clienteList;
}
public ArrayList<Cliente> getClienteList() {
return clienteList;
}
}
Testing My Marshalling
package mx.com.findep.crediseguros.dto.servicios.finsol;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class TestClientesXml {
private static final String SOME_XML = "C:/bea/user_projects/domains/DominioDesarrollo/esquemas/calculoCostoSeguroPeticion.xml";
public static void main(String[] args) throws JAXBException, IOException {
ArrayList<Cliente> clienteList = new ArrayList<Cliente>();
Cliente cliente1 = new Cliente();
cliente1.setNumeroPersona("1");
clienteList.add(cliente1);
Cliente cliente2 = new Cliente();
cliente2.setNumeroPersona("2");
clienteList.add(cliente2);
Clientes clientes = new Clientes();
clientes.setClienteList(clienteList);
JAXBContext context = JAXBContext.newInstance(Clientes.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(clientes, System.out);
m.marshal(clientes, new File(SOME_XML));
System.out.println();
System.out.println("Output from our XML File: ");
Unmarshaller um = context.createUnmarshaller();
Clientes clientes2 = (Clientes) um.unmarshal(new FileReader(SOME_XML));
ArrayList<Cliente> list = clientes2.getClienteList();
for (Cliente cliente : list) {
System.out.println("Cliente: " + cliente.getNumeroPersona());
}
}
}
By default JAXB treats public fields and properties as mapped. If you annotate a field it then considers the field and property as mapped causing the conflict. Without #XmlAccessorType(XmlAccessType.FIELD) you should annotate the get or set method.
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
So lets say we have:
#XmlRootElement(name = "book")
public class Book {
#XmlElement(name = "book_title")
private String title;
public getTitle(){..}
public setTitle(){...}
}
if we run the code we will have
Exception in thread "main"
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "title"
this problem is related to the following location:
at public java.lang.String com.example.jaxb.Book.getTitle()
at com.example.jaxb.Book
this problem is related to the following location:
at private java.lang.String com.example.jaxb.Book.title
at com.example.jaxb.Book
But if we add the annotation: XmlAccessorType
#XmlRootElement(name = "book")
#XmlAccessorType(XmlAccessType.FIELD)
public class Book {
the error will disappear.
When have class which want to marshall and have 10 fields, I prefer to annotate only fields, not one time the setter then the getter. So use #XmlAccessorType and annotate only the fields.

Binding XML using POJO and JAXB annotations

I have the following xml format that i want to bind it through a POJO and using JAXB annotations. The XML format is the following:
<datas>
<data>apple<data>
<data>banana<data>
<data>orange<data>
<datas>
And i'm trying to bind the data through the following POJO:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Datas {
#XmlElement
private List<String> data;
//get/set methods
}
And also i try and this POJO:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Datas {
#XmlElement
private List<Data> datas;
//get/set methods
}
//
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Data{
#XmlElement
private String data;
//get/set methods
}
In the first case it retrieves only the first data: apple. In the second case doesn't retrieve anything. Could someone help me to provide the appropriate POJO and annotations in order to bind all data?
You can do one of the following options:
OPTION #1
Datas
package forum11311374;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Datas {
private List<String> data;
//get/set methods
}
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html
OPTION #2
Datas
package forum11311374;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Datas {
#XmlElement(name="data")
private List<Data> datas;
//get/set methods
}
Data
package forum11311374;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Data{
#XmlValue
private String data;
//get/set methods
}
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
The following can be used with both options:
input.xml/Ouput
I have updated the XML document to contain the necessary closing tags. <data>apple</data> instead of <data>apple<data>.
<datas>
<data>apple</data>
<data>banana</data>
<data>orange</data>
</datas>
Demo
package forum11311374;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Datas.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11311374/input.xml");
Datas datas = (Datas) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(datas, System.out);
}
}
The first option did work for me... not sure why you are getting the problem...
Try this annotation...
#XmlElements(#XmlElement(name="data", type=String.class))
private List<String> datas; //ignore the variable name

#XmlRootElement and <T extends Serializable> throws IllegalAnnotationExceptions

When I marshall an instance of this class ...
#XmlRootElement
public static class TestSomething<T extends Serializable> {
T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
... the following Exception is thrown ...
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.io.Serializable is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.io.Serializable
at public java.io.Serializable TestSomething.getId()
at TestSomething
java.io.Serializable does not have a no-arg default constructor.
this problem is related to the following location:
at java.io.Serializable
at public java.io.Serializable TestSomething.getId()
at TestSomething
How can I avoid this (without changing the type parameter to something like <T>)?
You need to use a combination of #XmlElement and #XmlSchemaType:
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
#XmlRootElement
public class TestSomething<T extends Serializable> {
T id;
#XmlElement(type=Object.class)
#XmlSchemaType(name="anySimpleType")
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
If you run the following:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(TestSomething.class);
TestSomething<Integer> foo = new TestSomething<Integer>();
foo.setId(4);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
You will get:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<testSomething>
<id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:int">4</id>
</testSomething>
Here is a guide how to use interfaces with JAXB.
JAXB needs concrete classes, because it has to instantiate them when marshalling from XML. And if T is no concrete class, it can't be instantiated.
Adding #XmlAnyElement to the 'id' field (along with #XmlAccessorType(XmlAccessType.FIELD) annotation at class level) or adding the same for getter will resolve this. (But it makes the xml element's type any.)

Categories