I have an Animal class and an extension of Animal called AnimalExtension.
public class Animal
public class AnimalExtension extends Animal
The only difference between the two classes is that AnimalExtension has another instance variable called animalId. Animal does not have this instance variable.
I also have my own data type that I want to marshal and unamarshal to XML. This data type is called AnimalList. inside AnimalList, there is a list of Animals as an instance variable.
@XmlType(name = "AnimalList")
public class AnimalList{
private List<Animal> animalList;
....
animalList can contain both Animal and AnimalExtension. However, on the XML I dont want the element to be named as AnimalExtension; I want them all to have element name of Animal. I only want the extra attribute to show up when JAXB knows that the Animal is actually an instance of AnimalExtension. So if I have a list of that looks like
List<Animal> animalList = new LinkedList<Animal>();
AnimalExtension animalExtension = new AnimalExtension();
animalExtension.setAnimalId(1);
amimalExtension.setName("Don");
Animal animal = new Animal();
animal.setName("Mike");
animalList.add(animalExtension);
animalList.add(animal);
I want the XML to look like
<AnimalList>
<Animal name="Don" id="1" />
<Animal name="Mike" />
</AnimalList>
This is what I have tried to do
@XmlElements(
{
@XmlElement(name = "Animal", type = Animal.class),
@XmlElement(name = "Animal", type = AnimalExtension.class)
}
)
public List<Animal> getEntries() {
return animalList;
}
The code compiles but when I try running my server. It gives me this weird error that is so unrelated to what is going on (BeanCreationException). I tried making the name of the XmlElement to be different for each type and that works, but the challenge here is to make the name the same.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'encryptionPayloadContentProvider'
To map this use case you could leverage the following XmlAdapters:
AnimalAdapter
Since AnimalExtension is a super set of Animal we will use it to produce/consume XML. Then we will leverage the value of the animalId property to determine if an instance of Animal or AnimalExtension will be returned to AnimalList.
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class AnimalAdapter extends XmlAdapter<AnimalExtension, Animal> {
@Override
public Animal unmarshal(AnimalExtension animalExtension) throws Exception {
if(0 != animalExtension.getAnimalId()) {
return animalExtension;
}
Animal animal = new Animal();
animal.setName(animalExtension.getName());
return animal;
}
@Override
public AnimalExtension marshal(Animal animal) throws Exception {
if(animal.getClass() == AnimalExtension.class) {
return (AnimalExtension) animal;
}
AnimalExtension animalExtension = new AnimalExtension();
animalExtension.setName(animal.getName());
return animalExtension;
}
}
IdAdapter
We will need a second XmlAdapter to suppress animalId if its value is 0:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class IdAdapter extends XmlAdapter<String, Integer> {
@Override
public Integer unmarshal(String string) throws Exception {
return Integer.valueOf(string);
}
@Override
public String marshal(Integer integer) throws Exception {
if(integer == 0) {
return null;
}
return String.valueOf(integer);
}
}
Your model classes will be annotated as follows:
AnimalList
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement(name="AnimalList")
public class AnimalList {
private List<Animal> animalList = new ArrayList<Animal>();
@XmlElement(name="Animal")
@XmlJavaTypeAdapter(AnimalAdapter.class)
public List<Animal> getEntries() {
return animalList;
}
}
Animal
import javax.xml.bind.annotation.XmlAttribute;
public class Animal {
private String name;
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
AnimalExtension
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class AnimalExtension extends Animal {
private int animalId;
@XmlAttribute(name="id")
@XmlJavaTypeAdapter(IdAdapter.class)
public int getAnimalId() {
return animalId;
}
public void setAnimalId(int animalId) {
this.animalId = animalId;
}
}
Demo Code
The following demo code can be used to demonstrate this solution:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(AnimalList.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
AnimalList animalList = (AnimalList) unmarshaller.unmarshal(xml);
for(Animal animal : animalList.getEntries()) {
System.out.println(animal.getClass());
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(animalList, System.out);
}
}
The following output will be produced:
class AnimalExtension
class Animal
<?xml version="1.0" encoding="UTF-8"?>
<AnimalList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Animal name="Don" id="1"/>
<Animal name="Mike"/>
</AnimalList>
Related Information
You may find the following information useful:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With