I have a JAXB-annotated POJO like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Clazz implements Serializable {
#XmlElement(required = false)
private int a;
#XmlElement(required = false)
private int b;
}
I want to mark that either field a or field b is required. With my current set-up, none of them are required, but I want one of them to be present and not the other. How could I achieve it?
You can do the following with #XmlElementRefs
Domain Model
Clazz
import java.io.Serializable;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Clazz implements Serializable {
#XmlElementRefs({
#XmlElementRef(name="a", type=JAXBElement.class),
#XmlElementRef(name="b", type=JAXBElement.class)
})
private JAXBElement<Integer> aOrB;
}
ObjectFactory
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "a")
public JAXBElement<Integer> createA(Integer integer) {
return new JAXBElement<Integer>(new QName("a"), Integer.class, integer);
}
#XmlElementDecl(name = "b")
public JAXBElement<Integer> createB(Integer integer) {
return new JAXBElement<Integer>(new QName("b"), Integer.class, integer);
}
}
Demo Code
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Clazz.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum22502171/input.xml");
Clazz clazz = (Clazz) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(clazz, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<clazz>
<b>123</b>
</clazz>
Related
I know it doesn't make much sense, but I have to generate an XML from a Java object without the parent node of some elements, like explained below.
This is the example Java class model for the XML:
#XmlRootElement(name = "person")
public class PersonXml {
#XmlElement(name = "name")
private String name;
#XmlElement(name = "car")
private List<CarXml> cars;
.
#XmlRootElement(name = "car")
public class CarXml {
#XmlElement(name = "model")
private String model;
#XmlElement(name = "brand")
private String brand;
By default, if I generate the XML from an object of PersonXml like this:
StringWriter writer = new StringWriter();
JAXBContext ctx = JAXBContext.newInstance(PersonXml.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.marshal(xml, writer);
I would get:
<person>
<name>Pedro</name>
<car>
<model>Logan</model>
<brand>Renault</brand>
</car>
<car>
<model>Duster</model>
<brand>Renault</brand>
</car>
</person>
What I need is to remove the <car> tag, or even to prevent it to be generated at all.
I need the XML to be like this:
<person>
<name>Pedro</name>
<model>Logan</model>
<brand>Renault</brand>
<model>Duster</model>
<brand>Renault</brand>
</person>
Of course I could convert the XML to a String and remove the tags with replaceAll or something like this, but I was wondering if there is a nicer way to achieve this.
If you need to generate this output, you can use JAXB as follows:
1) Create a new Person class:
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"field"
})
#XmlRootElement(name = "person")
public class Person {
#XmlElementRefs({
#XmlElementRef(name = "name", type = JAXBElement.class, required = false),
#XmlElementRef(name = "model", type = JAXBElement.class, required = false),
#XmlElementRef(name = "brand", type = JAXBElement.class, required = false)
})
protected List<JAXBElement<String>> field;
public List<JAXBElement<String>> getNameOrModelOrBrand() {
if (field == null) {
field = new ArrayList<>();
}
return this.field;
}
}
2) Create an ObjectFactory to make it easier to use the person class:
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _PersonName_QNAME = new QName("", "name");
private final static QName _PersonModel_QNAME = new QName("", "model");
private final static QName _PersonBrand_QNAME = new QName("", "brand");
public ObjectFactory() {
}
public Person createPerson() {
return new Person();
}
#XmlElementDecl(namespace = "", name = "name", scope = Person.class)
public JAXBElement<String> createPersonName(String value) {
return new JAXBElement<>(_PersonName_QNAME, String.class, Person.class, value);
}
#XmlElementDecl(namespace = "", name = "model", scope = Person.class)
public JAXBElement<String> createPersonModel(String value) {
return new JAXBElement<>(_PersonModel_QNAME, String.class, Person.class, value);
}
#XmlElementDecl(namespace = "", name = "brand", scope = Person.class)
public JAXBElement<String> createPersonBrand(String value) {
return new JAXBElement<>(_PersonBrand_QNAME, String.class, Person.class, value);
}
}
Use the factory as follows:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
import java.util.List;
...
ObjectFactory factory = new ObjectFactory();
Person person = factory.createPerson();
List<JAXBElement<String>> list = person.getNameOrModelOrBrand();
list.add(factory.createPersonName("Pedro"));
list.add(factory.createPersonModel("Logan"));
list.add(factory.createPersonBrand("Renault"));
list.add(factory.createPersonModel("Duster"));
list.add(factory.createPersonBrand("Renault"));
JAXBContext ctx = JAXBContext.newInstance(Person.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
System.out.println(writer.toString());
The end result is XML as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<name>Pedro</name>
<model>Logan</model>
<brand>Renault</brand>
<model>Duster</model>
<brand>Renault</brand>
</person>
Creating elements in this way is the only way I know to get the end result you need.
There are probably various things you could do to refactor the above code, to streamline the creation of the list of elements - but this shows you the basic approach.
As you already know - this is far from ideal. The end result is not any type of XML that I would want to receive.
I wonder if someone can help me with a JAXB problem.
If I have an abstract class with 2 concrete implementations: For example (I have left out most of the markup/xml for brevity):
public abstract class Vehicle{}
public class Car extends Vehicle{}
public class Van extends Vehicle{}
Is there a way to have the xml below unmarshall correctly to the appropriate concrete class
<request>
<car>...</car>
</request>
rather than the following:
<request>
<vehicle xsi:type="car"></vehicle>
</request>
The reason I need this is to be backward compatible with our already published API.
Thanks in advance.
I have just answered in russian speaking community on similar question. Probably you looking for something like that:
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
Some quick example:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.StringReader;
import java.util.List;
public class Test {
public static void main(String... args) throws JAXBException {
String xmldata = "<request><car></car><van></van></request>";
StringReader reader = new StringReader(xmldata);
JAXBContext jaxbContext = JAXBContext.newInstance(Request.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Request request = (Request) unmarshaller.unmarshal(reader);
for (Vehicle object : request.getVehicles()) {
System.out.println(object.getClass());
}
}
}
#XmlRootElement(name = "request")
class Request {
private List<Vehicle> vehicles;
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
abstract class Vehicle {
}
class Van extends Vehicle {
}
class Car extends Vehicle {
}
The output will be:
class Car
class Van
UPD:
Update after comment. For single entry it will work anyway just remove List:
#XmlRootElement(name = "request")
class Request {
private Vehicle vehicles;
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public Vehicle getVehicles() {
return vehicles;
}
public void setVehicles(Vehicle vehicles) {
this.vehicles = vehicles;
}
}
Hope this will help.
You can use annotations and annotate the concrete implementations. In this case #XmlType() above Car or Van. This way you will keep your xml generic.
I've been experimenting with JAXB tutorials and have managed to get code working that generates an XML file from a Java object and then is able to use the XML to generate a Java object. At the moment it reads multiple instances of the same class to create an XML file similar to the one below
<Car>
<regplate>TR54</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Car>
<Car>
<regplate>BN04 THY</regplate>
<colour>yellow</colour>
<energyrating>3</energyrating>
</Car>
<Car>
<regplate>BN05 THY</regplate>
<colour>yellow</colour>
<energyrating>5</energyrating>
</Car>
I would like to be able to use the JAXB technology to work with subclasses. For example: Say I have a Car, Van and Bicycle objects that are subclasses of Vehicle. Is it possible for me to manipulate my JAXB class to write an XML file that would produce something similar to this? I have provided the code I am working with below.
<Vehicle>
<Car>
<regplate>TR54</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Car>
<Van>
<regplate>MN05 RFD</regplate>
<colour>red</colour>
<energyrating>5</energyrating>
</Van>
<Car>
<regplate>ZX54 UJK</regplate>
<colour>red</colour>
<energyrating>1</energyrating>
</Car>
</Vehicle>
Main Class
package basictransport2;
public class Main
{
public static void main(String[] args)
{
JAXB parser = new JAXB();
parser.marshall();
//parser.unmarshallList();
}
}
Vehicle Class
package basictransport2;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
//#XmlRootElement(name = "Vehicle")
public class Vehicle
{
private int ownerId;
public Vehicle(int ownerId)
{
this.setOwnerId(ownerId);
}
//#XmlElement (name = "Owner ID")
public int getOwnerId()
{
return ownerId;
}
public void setOwnerId(int ownerId)
{
this.ownerId = ownerId;
}
public int getEnergyRating()
{
return (Integer) null;
}
public String getColour()
{
return null;
}
public String getRegPlate()
{
return null;
}
}
Car Class
package basictransport2;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
//#XmlRootElement(name = "Car")
public class Car extends Vehicle
{
private String regPlate;
private int energyRating;
private String colour;
public Car(String regPlate, int energyRating, String colour, int ownerId)
{
super(ownerId);
this.regPlate = regPlate;
this.energyRating = energyRating;
this.colour = colour;
}
public Car(int ownerId)
{
super(ownerId);
}
//#XmlElement (name = "Registration")
public String getRegPlate()
{
return regPlate;
}
public void setRegPlate(String regPlate)
{
if(this.regPlate == null)
{
this.regPlate = regPlate;
}
}
//#XmlElement (name = "Energy Rating")
public int getEnergyRating()
{
return energyRating;
}
public void setEnergyRating(int energyRating)
{
this.energyRating = energyRating;
}
//#XmlElement (name = "Colour")
public String getColour()
{
return colour;
}
public void setColour(String colour)
{
this.colour = colour;
}
}
JAXB Class
package basictransport2;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class JAXB
{
public void marshall()
{
try
{
List<Vehicle> vehicleList = new ArrayList<Vehicle>();
vehicleList.add(new Car("SG09 TYH", 4, "Yellow", 1));
vehicleList.add(new Car("XX09 VVV", 3, "Red", 2));
vehicleList.add(new Car("BL09 TYZ", 4, "Blue", 3));
Garage listOfVehicles = new Garage();
listOfVehicles.setListOfVehicles(vehicleList);
JAXBContext context = JAXBContext.newInstance(Garage.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(listOfVehicles, System.out);
marshaller.marshal(listOfVehicles, new File("src\\data\\listcar.xml"));
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
}
public void unmarshall()
{
try
{
JAXBContext context = JAXBContext.newInstance(Garage.class);
Unmarshaller unmarhsaller = context.createUnmarshaller();
Garage listOfVehicles = (Garage)unmarhsaller.unmarshal(new File("src\\data\\listcar.xml"));
System.out.println("List Car information");
for(Vehicle vehicle : listOfVehicles.getListOfVehicles())
{
System.out.println("Reg Plate: " + vehicle.getRegPlate());
System.out.println("Energy Rating: " + vehicle.getEnergyRating());
System.out.println("Colour: " + vehicle.getColour());
System.out.println("================");
}
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
}
}
List class
package basictransport2;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Vehicle")
public class Garage
{
#XmlElements
({
#XmlElement(name = "Car", type = Car.class, required = false)
})
private List<Vehicle> vehicleCollection = new ArrayList<Vehicle>();
public List<Vehicle> getListOfVehicles()
{
return vehicleCollection;
}
public void setListOfVehicles(List<Vehicle> listOfVehicles)
{
this.vehicleCollection = listOfVehicles;
}
}
Thanks everyone for your input. I used feedback from all your answers but ultimately it was a combination of them that worked which is why I created a seperate answer for anyone who may have this problem in the future.
To get this to work I had to ensure that all getter methods within the super and sub classes being marhsalled/unmarshalled were annotated with #XmlElement. This would determine the XML tag for the corresponding variable.
#XmlElement (name = "OwnerID")
public int getOwnerId()
{
return ownerId;
}
The superclass had to be annotated with #XmlSeeAlso to bind the subclasses to it. i.e In my code RoadVehicle was the superclass and both the Car and Van classes extended it.
#XmlSeeAlso({Car.class, Van.class})
public class Vehicle
{
With the super and subclasses now annotated the only other class that required annotations was the list class (Garage in my code). The changes here would determine what the XML tags were populated with.
The root XML tag was set by applying the #XmlRootElement annotation to the top of the class. i.e. "Vehicle" would be the root XML tag in my example.
#XmlRootElement(name = "Vehicle")
public class Garage
{
Finally an #XmlElements list had to be declared with an #XmlElements annotation for each sub class that required an XML tag with the name supplying the name of the XML tag. This list had to be declared above the getter method for the collection.
#XmlElements
({
#XmlElement(name = "Car", type = Car.class, required = false),
#XmlElement(name = "Van", type = Van.class, required = false)
})
public List<Vehicle> getListOfVehicles()
{
return vehicleCollection;
}
you are on right track. May something below will help
#XmlRootElement(name = "car")
public class Car extends BasicType{
}
#XmlRootElement(name = "van")
public class Van extends BasicType{
}
#XmlRootElement(name = "vehicle")
public class Vehicle {
List<BasicType> basicType;
}
The simplest solution is to have different subclasses for cars and vans, even it they don't add anything to the base classes. Then, the root element class contains a list of the base class, with element QNames identifying the actual class.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Vehicle")
public class Vehicle {
#XmlElements({
#XmlElement(name = "Car", type = Car.class, required = false),
#XmlElement(name = "Van", type = Van.class, required = false)
})
protected List carOrVan;
public List getCarOrVan() {
if (carOrVan == null) {
carOrVan = new ArrayList();
}
return this.carOrVan;
}
}
Here's the base class and the subclasses:
public class Basic {
private String regplate;
private String color;
private String energyrating;
public String getRegplate(){ return regplate; }
public void setRegplate( String v ){ regplate = v; }
public String getColor(){ return color; }
public void setColor( String v ){ color = v; }
public String getEnergyrating(){ return energyrating; }
public void setEnergyrating( String v ){ energyrating = v; }
}
public class Car extends Basic {}
public class Van extends Basic {}
This will go smoothly if cars and vans develop into distinct subclasses.
In the root.class from my xi-schema, the element item and ohter objects are part of an itemList:
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
//...
protected List<Object> itemList;
I've in the ObjectFactory.class from the main-schema some items as JAXBElements like this:
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<numItemType> createDetailedInformation(numItemType num) {
return new JAXBElement<numItemType>(_detailedInformation_QNAME, numItemType.class, null, num);
}
So the numItemType has some attributes and value(num) for the JAXBElement.
NumItemType.class:
#XmlJavaTypeAdapter(numItemTypeAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
But when JAXB unmarshal the XML document, it will has only elements, for example:
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
When I marshal it, it should become (like the JAXB java code):
<detailedInformation element2="5678" element3="bla">1234</detailedInformation>
Therefore, I have written an numItemTypeAdapter.class with
NumItemTypeAdapter extends XmlAdapter
AdaptedNum.class:
public class AdaptedNum {
#XmlElement
private double element1;
#XmlElement
private String element2;
#XmlElement
private String element3;
/** Some getter/setter methods */
}
I thought, that would be help me http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html, but it is all a bit tricky :-/
It's a bit tricky to sort out exactly where your problem may be occurring. I'm assuming your original model was generated from an XML Schema, this should work as is without any modifications. I've attempted below to provide a scaled down version of your example which may help.
Root
package forum11343610;
import java.util.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
protected List<Object> itemList = new ArrayList<Object>();
public List<Object> getItemList() {
return itemList;
}
public void setItemList(List<Object> itemList) {
this.itemList = itemList;
}
}
NumItemType
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
ObjectFactory
package forum11343610;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private static final QName _detailedInformation_QNAME = new QName("de-schema", "detailedInformation");
#XmlElementDecl(namespace = "xi", name = "item")
public JAXBElement<NumItemType> createItem(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<NumItemType> createDetailedInformation(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
}
Demo
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
ObjectFactory objectFactory = new ObjectFactory();
Root root = new Root();
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
Thank your for your comment.
That's the point:
Demo
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
It should unmarshal and map the XML automatically.
XML Input
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
With the Code:
Demo
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
Unmarshaller u = jc.createUnmarshaller();
File xml = new File("D:/", "test.xml");
Root root = (Root) u.unmarshal(xml);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
I could parse the XML Document with DOM to a tree and marhalling with JAXB...
Thank you!
In other words:
I want to set new elements to the NumItemType.class for the unmarshalling without change the schema java code.
NumItemType.class
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
#XmlJavaTypeAdapter(NumItemTypeAdapter.class)
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
NumItemTypeAdapter.class
public class NumItemTypeAdapter extends XmlAdapter<AdaptedNum, NumItemType> {
#Override
public NumItemType unmarshal(AdaptedNum an) throws Exception {
NumItemType nit = new NumItemType();
nit.setNum(an.getNum);
nit.setPrecision(an.getPrecision);
nit.setDecimals(an.getDecimals)
return nit;
}
}
AdaptedNum.class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "adaptedNum", namespace = "", propOrder = {
"element1",
"element2",
"element3"
})
public class AdaptedNum {
#XmlElement(name ="element1")
protected BigDecimal num;
#XmlElement(name ="element2")
private String decimals;
#XmlElement(name ="element3")
private String precison;
// set/get method
}
I am having issues to unmarshall nested xml below. Can someone please advise if I am missing something.
body tag can contain any Jaxb anotated obj.
Do I have to create a custom adapter for marshalling/unmarshalling such xml?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<serviceRq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="serviceRq">
<body>
<createRq>
<id>1234</id>
</createRq>
</body>
</serviceRq>
My Jaxb-annotated classes are:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
private Object body;
<!-- getters and setters omitted-->
}
Here, body can be any jaxb annotated object, in this case its CreateRq.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
<!-- getters and setters omitted-->
}
I am looking for a generic way to support any Jaxb annotated object in body of the input xml.
You could use a #XmlAnyElement(lax=true) and an XmlAdapter to handle this use case:
ServiceRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlJavaTypeAdapter(value=BodyAdapter.class)
private Object body;
// getters and setters omitted
}
BodyAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BodyAdapter extends XmlAdapter<Body, Object>{
#Override
public Object unmarshal(Body v) throws Exception {
return v.getValue();
}
#Override
public Body marshal(Object v) throws Exception {
Body body = new Body();
body.setValue(v);
return body;
}
}
Body
import javax.xml.bind.annotation.XmlAnyElement;
public class Body {
private Object value;
#XmlAnyElement(lax=true)
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
Demo
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(ServiceRq.class);
System.out.println(jc);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
http://bdoughan.blogspot.com/2010/12/represent-string-values-as-element.html
You could use #XmlAnyElement(lax=true) and the #XmlPath extension in EclipseLink JAXB (MOXy) to handle this use case (Note: I'm the MOXy lead). For an approach that would work with any JAXB implementation (Metro, MOXy, JaxMe, etc) see: Jaxb complex xml unmarshall.
ServiceRq
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlPath("body/createRq")
#XmlAnyElement(lax=true)
private Object body;
// getters and setters omitted
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
Demo
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(ServiceRq.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
jaxb.properties
To use MOXy as your JAXB provider you must include a file named jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html