I am using JAXB to unmarshal an XML file.
All I know about the XML file is that it is valid XML.
How then am I supposed to specify a class and/or package to newInstance?
JAXBContext jaxbContext = JAXBContext.newInstance(??????);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object o = (Object) unmarshaller.unmarshal(myFile);
I did not see anything in the docs that address this issue.
You need to tell JaxB what class to unmarshall to so that it can use the annotations in the class to resolve the hierarchy of the xml. You will need to have a class that is also annotated with something like #XmlRootElement. If you want to parse arbitrary xml you will probably need to do something with a DocumentBuilder or xpath.
See this artical for more info.
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html
I have used something like this to convert arbitrary xml to a class. The any field will actually be a list of org.w3c.dom.Element in which you can get information from.
http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Element.html
#XmlRootElement
class Wrapper {
/**
* Everything else
*/
#Transient
#XmlAnyElement(lax = true)
private List<Element> any;
public List<Element> getAny() {
return any;
}
}
In newInstance you must add the class root element that map your xml... below an example
Here an example ..
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(Vehicals.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final Vehicals vehicals = new Vehicals();
List<Car> cars = new ArrayList<Car>();
Car c = new Car();
c.setName("Mercedes");
cars.add(c);
c = new Car();
c.setName("BMW");
cars.add(c);
vehicals.setCar(cars);
m.marshal(vehicals, System.out);
}
Vehicals.java
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Vehicals {
private List<Car> Car;
public List<Car> getCar() {
return Car;
}
public void setCar(List<Car> cars) {
this.Car = cars;
}
}
Car.java
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement
public class Car {
#XmlTransient
private Long id;
private String name;
#XmlTransient
private String code;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
output.xml
<Vehicle>
<Car>
<name>Mercedes</name>
</Car>
<Car>
<name>BMW</name>
</Car>
</Vehicle>
For the Unmarshal is the same thing. In my case i added Vehicals as parameter in newInstance method.
Related
I have a JSON file that I am trying to convert into XML using the JAXB annotation approach. Everything is working fine now and I able to convert the JSON to XML. Now I am trying to refactor the code a little bit so that my class would look clean. Hence, I am trying to remove the method which is present in my class and make it JAXB XMLAdapter so that it can be reused by other classes.
Basically I would like to move the XMLSupport method from CarInfo class to XMLAdapter. I am not sure how to populate the CarInfo objects when I move them to the XMLAdapter.
Following is my JSON file (it has been modified for simplicity purpose):
{
"brand": "Ferari",
"build": "Italy",
"engine": "Mercedes",
"year": "2021"
}
Following is the XML that I expect JAXB to provide: (Observe the carInfo tag which is not present in JSON but I need in XML to match the standard XSD)
<?xml version="1.0"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Following are the classes that I have: (Tha Car class that matches the JSON elements)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlTransient
#XmlSeeAlso({MyCar.class});
public class Car{
private String brand;
private String build;
#XmlTransient
private String engine;
#XmlTransient
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is MYCar class that builds the XML by adding the carInfo tag:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Car")
#XmlType(name = "Car", propOrder = {"brand","build", "carInfo"})
public class MyCar extends Car{
#XmlElement(name="carInfo")
private CarInfo carInfo;
public MyCar xmlSupport() {
if(carInfo == null){
carInfo = new Carinfo();
}
carInfo.setEngine(getEngine);
carInfo.setYear(getYear());
return this;
}
}
Following is my CarInfo class which acts as a helper to build the additional tag around MyCar class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {"engine","year"})
public class Carinfo{
private String engine;
private String year;
//Getter, Setters and other consturctiores ommited
}
Following is my Main class which actually builds the XML by using the JAXBCOntext
public class Main{
public static void main(String[] args){
JAXBContext context = JAXBContext.newInstance(MyCar.class);
Marshaller mar = context.createMarshaller();
mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal((MyCar).xmlSupport(), System.out);
System.out.println("-----------------");
}
}
Now coming back to my main question:
As we can see from MyCar class I have the XMLSupport method which is actually populating the CarInfo objects and then using that method I am creating the XML. Is there a way I can move this to XMLAdapter?
I tried creating the XMLAdapter but I am not sure how can I populate the CarInfo objects from the adapter:
public class MyCar extends Car{
#XmlElement(name="carInfo")
#XmlJavaTypeAdapter(ExtensionAdapter.class)
#XmlElement(name = "carInfo")
private CarInfo carInfo;
}
Following is my Adapter class I've tried:
public class ExtensionAdapter extends XmlAdapter<CarInfo, CarInfo> {
#Override
public CarInfo unmarshal(CarInfo valueType) throws Exception {
System.out.println("UN-MARSHALLING");
return null;
}
#Override
public CarInfo marshal(CarInfo boundType) throws Exception {
System.out.println("MARSHALLING");
System.out.println(boundType);
//I get boundType as NULL so I am not sure how to convert the xmlSupport Method to Adapter so I can use this adapter with multiple class
return null;
}
}
You don't need any adapters, you just need a well-defined POJO.
The trick is using getters and setters, not field access, so we can do delegation, and then use #JsonIgnore and #XmlTransient to control which getter/setter methods are used for JSON vs XML.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#XmlRootElement(name = "Car")
#XmlType(propOrder = { "brand", "build", "carinfo" })
#JsonPropertyOrder({ "brand", "build", "engine", "year" })
public final class Car {
#XmlType(propOrder = { "engine", "year" })
public static final class Info {
private String engine;
private String year;
public String getEngine() {
return this.engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getYear() {
return this.year;
}
public void setYear(String year) {
this.year = year;
}
#Override
public String toString() {
return "Info[engine=" + this.engine + ", year=" + this.year + "]";
}
}
private String brand;
private String build;
private Info carinfo;
public Car() {
// Nothing to do
}
public Car(String brand, String build, String engine, String year) {
this.brand = brand;
this.build = build;
this.carinfo = new Info();
this.carinfo.setEngine(engine);
this.carinfo.setYear(year);
}
public String getBrand() {
return this.brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getBuild() {
return this.build;
}
public void setBuild(String build) {
this.build = build;
}
#JsonIgnore // For XML, not JSON
public Info getCarinfo() {
if (this.carinfo == null)
this.carinfo = new Info();
return this.carinfo;
}
public void setCarinfo(Info info) {
this.carinfo = info;
}
#XmlTransient // For JSON, not XML
public String getEngine() {
return getCarinfo().getEngine();
}
public void setEngine(String engine) {
getCarinfo().setEngine(engine);
}
#XmlTransient // For JSON, not XML
public String getYear() {
return getCarinfo().getYear();
}
public void setYear(String year) {
getCarinfo().setYear(year);
}
#Override
public String toString() {
return "Car[brand=" + this.brand + ", build=" + this.build + ", carinfo=" + this.carinfo + "]";
}
}
Test
Car car = new Car("Ferari", "Italy", "Mercedes", "2021");
// Generate JSON
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.enable(SerializationFeature.INDENT_OUTPUT);
String json = jsonMapper.writeValueAsString(car);
// Generate XML
JAXBContext jaxbContext = JAXBContext.newInstance(Car.class);
Marshaller xmlMarshaller = jaxbContext.createMarshaller();
xmlMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
String xml;
try (StringWriter writer = new StringWriter()) {
xmlMarshaller.marshal(car, writer);
xml = writer.toString();
}
// Print generated results
System.out.println(car);
System.out.println(json);
System.out.println(xml);
// Parse JSON
Car carFromJson = jsonMapper.readValue(json, Car.class);
System.out.println(carFromJson);
// Parse XML
Unmarshaller xmlUnmarshaller = jaxbContext.createUnmarshaller();
Car carFromXml = xmlUnmarshaller.unmarshal(new StreamSource(new StringReader(xml)), Car.class).getValue();
System.out.println(carFromXml);
Outputs
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
{
"brand" : "Ferari",
"build" : "Italy",
"engine" : "Mercedes",
"year" : "2021"
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Car>
<brand>Ferari</brand>
<build>Italy</build>
<carinfo>
<engine>Mercedes</engine>
<year>2021</year>
</carinfo>
</Car>
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
Car[brand=Ferari, build=Italy, carinfo=Info[engine=Mercedes, year=2021]]
As you can see, the generated JSON and XML is exactly what you wanted, and the last two lines of output shows that parsing works as well.
I wan't to deserialize XML to my POJO but something doing wrong...
My POJO class:
#Builder
#ToString
#AllArgsConstructor
#NoArgsConstructor
#XmlRootElement(name="taxi")
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(propOrder = {"id", "name", "phone", "citiesId"})
public class TaxiEntity {
#Getter #Setter
private Integer id;
#Getter #Setter
private String name;
#Getter #Setter
private String phone;
#Singular("city")
private Set<Integer> citiesId = new HashSet<>();
#XmlElementWrapper(name="cities_id")
#XmlElement(name="city_id")
public void setCitiesId(Set<Integer> citiesId) {
System.out.println("setCitiesId()");
this.citiesId = citiesId;
}
public Set<Integer> getCitiesId() {
System.out.println("getCitiesId()");
return new HashSet<>(citiesId);
}
}
Marshalling example:
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
TaxiEntity entity = TaxiEntity.builder().
id(5).
name("my city").
phone("12345678").
city(1).
city(5).
build();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(entity, new File("entity.xml"));
XML output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<taxi>
<id>5</id>
<name>my city</name>
<phone>12345678</phone>
<cities_id>
<city_id>1</city_id>
<city_id>5</city_id>
</cities_id>
</taxi>
Unmarshalling example:
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
TaxiEntity entity = (TaxiEntity) unmarshaller.unmarshal(new File("entity.xml"));
System.out.println(entity);
Console output:
getCitiesId()
getCitiesId()
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[])
Process finished with exit code 0
As you can see, citiesId is empty.
It happens because JAXB unmarshalling calling the getter (copy of field in my case)
and trying to set values into a copy of collection.
How to make it create a collection and set it via setter?
P.S. In my real bussiness object, i have collect IDs in getter from DB entities, and cannot return collection in getter.
Thanks!
---- Edited last time -----
import java.io.File;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import lombok.Builder;
import lombok.Singular;
import lombok.ToString;
#Builder
#ToString
#XmlRootElement(name = "taxi")
#XmlType(name="taxi", propOrder = { "id", "name", "phone", "citiesId" })
public class TaxiEntity {
private Integer id;
private String name;
private String phone;
#Singular("city")
private Set<Integer> citiesId;
public TaxiEntity() {
}
public TaxiEntity(Integer id, String name, String phone, Set<Integer> citiesId) {
System.out.println("Hello");
this.id = id;
this.name = name;
this.phone = phone;
this.citiesId = citiesId;
}
#XmlElementWrapper(name = "cities_id")
#XmlElement(name = "city_id")
public void setCitiesId(Set<Integer> citiesId) {
System.out.println("I should be calling during deserialization" + citiesId);
this.citiesId = citiesId;
}
#XmlElement
public void setId(Integer id) {
this.id = id;
}
#XmlElement
public void setName(String name) {
this.name = name;
}
#XmlElement
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public Set<Integer> getCitiesId() {
System.out.println("Calling getter " + this.citiesId);
return citiesId;
}
public static void main(String[] args) {
try {
JAXBContext context = JAXBContext.newInstance(TaxiEntity.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
Unmarshaller unmarshaller = context.createUnmarshaller();
TaxiEntity entity = TaxiEntity.builder().id(5).name("my city").phone("12345678").city(1).city(5).build();
marshaller.marshal(entity, new File("C:/whee/entity.xml"));
System.out.println("Unmarshalling now ------");
TaxiEntity taxEntityWithSettersGetters = (TaxiEntity) unmarshaller.unmarshal(new File("C:/whee/entity.xml"));
System.out.println(taxEntityWithSettersGetters);
} catch (Exception e) {
e.printStackTrace();
}
}
Printout:
Hello
Calling getter [1, 5]
Unmarshalling now ------
Calling getter null
I should be calling during deserialization[]
Calling getter [1, 5]
TaxiEntity(id=5, name=my city, phone=12345678, citiesId=[1, 5])
During unmarshalling JAXB checks if your collection is null, if it is (It will call the setter for the first time to initialize it to empty), and you can see that in the log.
However, afterwards, it will use its internal logic to populate the collection (SET), initialize its type (New Set)*by using the Setter you have, and use the Set.add(xyz); to add (1), then (5).
The JAXB Logic invoked is found in class:
public abstract class Lister<BeanT,PropT,ItemT,PackT> {
//startPacking is calling to initialize the collection Set so it is empty
public T startPacking(BeanT bean, Accessor<BeanT, T> acc) throws AccessorException {
T collection = acc.get(bean);
if(collection==null) {
collection = ClassFactory.create(implClass);
if(!acc.isAdapted())
acc.set(bean,collection);
}
collection.clear();
return collection;
}
//Right way, this gets called afterwards (Before any of your TaxiEntity logic), to do addToPack(1), addToPack(5), <--- Now your Set has [1,5]
public void addToPack(T collection, Object o) {
collection.add(o);
}
Then, you see in the log, it calls getCitiesIds(), and you will see magically it has [1,5]
Its the way JAXB works with Collections. All other elements, their proper Setters are called.
See, JAXB does not call Setter method
You need to think of a different way of doing it, rather than dependending on the getter/setter. It did its job of unmarshalling the object from the XML file, the rest of the logic could be written in an external method.
I want to read in a xml-file based on jaxb to my objectoriented structure.
Lets say this is my xml-file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<children xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<child xsi:type="girl">
<age>12</age>
<isdancing>true</isdancing>
</child>
<child xsi:type="boy">
<age>10</age>
<issoccerplayer>true</issoccerplayer>
</child>
</children>
children is some kind of wrapper element including multiple child elements. A child can either be a boy or a girl specified by xsi:type. These two classes have some elements in common (like age) and some different (excluding) elements (like isdancing or issoccerplayer)
To read the file, i have this method:
public static void main( String[] args ) throws JAXBException
{
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
File file = new File("C:/test.xml");
if (!file.exists()) System.out.println("File does not exist");
Children children = (Children) jaxbUnmarshaller.unmarshal(file);
System.out.println(children.toString());
}
My Children class looks like this:
#XmlRootElement(name="children")
#XmlAccessorType(XmlAccessType.FIELD)
public class Children {
#XmlElement(name="child")
private List<Child> childrenList;
public List<Child> getChildren() { return childrenList; }
public void setChildren(List<Child> children) {this.childrenList = children;}
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
My Child class looks like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Child {
#XmlAttribute(name="xsi:type")
private XsiType xsiType;
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
//Getter and setter for all fields
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
}
And my XsiType class looks like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class XsiType {
#XmlAttribute(name="xsi:type")
private String name;
#XmlValue
private String value;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value;
public void setValue(String value) { this.value = value; }
}
In my pom.xml i have included the following dependencies:
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies>
My problem is now, that the output is ok, but the element xsiType of Child-class is always null or otherwise it ends up in IllegalAnnotationExceptions, which are related to XmlTest.model.Child.xsiType
So i expect there is a mistake by setting any kind of #Xml-Annotation. Can somebody help me by finding the mistake?
The target is to iterate of the list of children and decide at runtime (based on the xsiType), if this is a girl or a boy.
Thanks
You don't need your XsiType class.
You can just use String instead.
In your Child class
the xsiType attribute should look like this.
#XmlAttribute(name = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
private String xsiType;
Notice: in the #XmlAttribute annotation
use name = "type" (without the prefix xsi:)
specify the namespace parameter as given in your XML
by xmlns:xsi="..."
By the way:
Instead of typing the string "http://www.w3.org/2001/XMLSchema-instance"
you should better use the constant
XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.
So your improved code would like this:
#XmlAttribute(name = "type", namespace = XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI)
private String xsiType;
xsi type is usually used to express references to concrete types. Jaxb can use xsi types without further workarounds.
Create a Boy and a Girl class that extend Children. (You might need to adjust the type names with #XmlType). With that, all elements with xsi:type=Girl will be bound to the class Girl
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class }) // Either use #XmlSeeAlso to register classes in the JaxbContext
// or add them to the context directly
public class Child {
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
// Getter and setter for all fields
}
#XmlType(name = "boy") // can be omitted if default value matches with the default value
public class Boy extends Child {
}
#XmlType(name = "girl")
public class Girl extends Child {
}
Complete selfcontained example:
package jaxb;
import java.io.File;
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
public class Inheritance {
public static void main(String[] args) throws JAXBException {
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
String x = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n"
+ " <children xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n"
+ " <child xsi:type=\"girl\">\r\n" + " <age>12</age>\r\n"
+ " <isdancing>true</isdancing>\r\n" + " </child>\r\n"
+ " <child xsi:type=\"boy\">\r\n" + " <age>10</age>\r\n"
+ " <issoccerplayer>true</issoccerplayer>\r\n" + " </child>\r\n" + " </children>";
Children children = (Children) jaxbUnmarshaller.unmarshal(new StringReader(x));
System.out.println(children.getChildren().toString());
}
#XmlRootElement(name = "children")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Children {
#XmlElement(name = "child")
private List<Child> childrenList;
public List<Child> getChildren() {
return childrenList;
}
public void setChildren(List<Child> children) {
this.childrenList = children;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class })
public static class Child {
private int age;
#XmlElement(name = "isdancing")
private boolean isDancing;
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
// Getter and setter for all fields
}
#XmlType(name = "boy")
public static class Boy extends Child {
}
#XmlType(name = "girl")
public static class Girl extends Child {
}
}
Clean solution for second approach (based on separate class-files):
public class App
{
public static void main(String[] args) throws JAXBException
{
JAXBContext jaxbContext = JAXBContext.newInstance(Children.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
File file = new File("C:/test2.xml");
Children children = (Children) jaxbUnmarshaller.unmarshal(file);
for (Child c : children.getChildren()) {
if (c instanceof Boy) {
System.out.println(((Boy)c).toString());
} else if (c instanceof Girl){
System.out.println(((Girl)c).toString());
}
}
}
}
Children.java
#XmlRootElement(name="children")
#XmlAccessorType(XmlAccessType.FIELD)
public class Children {
#XmlElement(name="child")
private List<Child> childrenList;
public List<Child> getChildren() { return childrenList; }
public void setChildren(List<Child> children) {this.childrenList = children;}
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Boy.java
#XmlType(name="boy")
public class Boy extends Child {
#XmlElement(name = "issoccerplayer")
private boolean isSoccerPlayer;
public boolean isSoccerPlayer() { return isSoccerPlayer; }
public void setSoccerPlayer(boolean isSoccerPlayer) { this.isSoccerPlayer = isSoccerPlayer; }
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Girl.java
#XmlType(name="girl")
public class Girl extends Child {
#XmlElement(name = "isdancing")
private boolean isDancing;
public boolean isDancing() { return isDancing; }
public void setDancing(boolean isDancing) { this.isDancing = isDancing; }
#Override
public String toString() { return ReflectionToStringBuilder.toString(this); }
}
Child.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ Boy.class, Girl.class })
public abstract class Child {
private int age;
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
The output should be:
de.home.myproject.XmlTest.model.Girl#12edcd21[isDancing=true,age=12]
de.home.myproject.XmlTest.model.Boy#27bc2616[isSoccerPlayer=true,age=10]
I have 3 input XML that have, pretty much, the same elements and attributes, in fact, they represent the same thing, so I want to marshall them to the same object, something like this:
Request One:
<?xml version="1.0" encoding="UTF-8"?>
<RequestOne>
<id>123</id>
<name>foo</name>
</RequestOne>
Request Two:
<?xml version="1.0" encoding="UTF-8"?>
<RequestTwo>
<id>123</id>
<value>val</value>
</RequestTwo>
Request Three:
<?xml version="1.0" encoding="UTF-8"?>
<RequestThree>
<name>foo</name>
<value>val</value>
</RequestThree>
Desired Object (something like):
#XmlRootElement
public class Resource{
#XmlElement
private String id;
#XmlElement
private String name;
#XmlElement
private String value;
//(...) more code
}
But I can't use multiple RootElement annotations to ask JAXB to unmarshall all of the 3 request to objects of the class Resource
Is there a way to do it? Or I must make the 3 sepparated classes?
Thanks for your help
Option 1
Unmarshal using the overloaded Generic unmarshal method :
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Base.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
JAXBElement<Base> basea = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootA><name>nanana</name></RootA>")), Base.class);
System.out.println(basea.getValue().getName());
JAXBElement<Base> baseb = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootB><name>nbnbnb</name></RootB>")), Base.class);
System.out.println(baseb.getValue().getName());
}
Option 2
You can always use Java's class subtyping capabilites ? JAXB does annotation scanning on parent class as well. This example works
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
#XmlRootElement( name = "RootA")
public static class RootA extends Base{
}
#XmlRootElement( name = "RootB")
public static class RootB extends Base {
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(RootA.class,RootB.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
RootA rootA = (RootA)unmarshaller.unmarshal(new StringReader("<RootA><name>nanana</name></RootA>"));
System.out.println(rootA.getName());
RootB rootB = (RootB)unmarshaller.unmarshal(new StringReader("<RootB><name>nbnbnb</name></RootB>"));
System.out.println(rootB.getName());
}
I'm trying to marshal an object that has an Object as one of its fields.
#XmlRootElement
public class TaskInstance implements Serializable {
...
private Object dataObject;
...
}
The dataObject can be one of many different unknown types, so specifying each somewhere is not only impractical but impossible. When I try to marshal the object, it says the class is not known to the context.
MockProcessData mpd = new MockProcessData();
TaskInstance ti = new TaskInstance();
ti.setDataObject(mpd);
String ti_m = JAXBMarshall.marshall(ti);
"MockProcessData nor any of its super class is known to this context." is what I get.
Is there any way around this error?
JAXB cannot marshal any old object, since it doesn't know how. For example, it wouldn't know what element name to use.
If you need to handle this sort of wildcard, the only solution is to wrap the objects in a JAXBElement object, which contains enough information for JAXB to marshal to XML.
Try something like:
QName elementName = new QName(...); // supply element name here
JAXBElement jaxbElement = new JAXBElement(elementName, mpd.getClass(), mpd);
ti.setDataObject(jaxbElement);
Method:
public String marshallXML(Object object) {
JAXBContext context;
try {
context = JAXBContext.newInstance(object.getClass());
StringWriter writer = new StringWriter();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, writer);
String stringXML = writer.toString();
return stringXML;
} catch (JAXBException e) {
}
}
Model:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
String name;
int id;
public String getName() {
return name;
}
#XmlElement
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
#XmlAttribute
public void setId(int id) {
this.id = id;
}
}