#XmlRootElement on class but not in xml - java

I have a class that I'm using to generate an xml payload from:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "someName", propOrder = {
"one",
"two"
})
#XmlRootElement(name = "test")
public class MyClass {
#XmlElement
protected String one;
#XmlElement
protected String two;
...
}
With an object factory method as follows
#XmlElementDecl("Something")
public JAXBElement<MyClass> getMyClassXml(MyClass value) {
return new JAXBElement<MyClass>(_Something_QNAME, MyClass.class, null, value);
}
I would like the soap body to contain
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>
but I end up with
<Something>
<one>1</one>
<two>2</two>
</Something>
Has anyone come across something similar?

If you put your class into a list and then save that list in XML it will come out similar to what you're looking for.

You can try the following code.
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)
public class MyClass {
#XmlElement
protected String one;
#XmlElement
protected String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Something")
#XmlAccessorType(XmlAccessType.FIELD)
public class Something {
#XmlElement(name = "test")
private MyClass testClass;
public MyClass getTestClass() {
return testClass;
}
public void setTestClass(MyClass testClass) {
this.testClass = testClass;
}
}
Test class to verify.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main(String[] args) throws Exception {
MyClass testClass = new MyClass();
testClass.setOne("1");
testClass.setTwo("2");
Something something = new Something();
something.setTestClass(testClass);
JAXBContext jaxbContext = JAXBContext.newInstance(Something.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(something, System.out);
}
}
After running the test class, you will find the following xml structure.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>
So, in your case, Something should contain MyClass as nested object to generate the desired XML structure using Jaxb.

try with this,
Something.java
#XmlRootElement
public class Something {
#XmlElement(name = "test")
private List<Test> testList;
public List<Test> getTestList() {
return testList;
}
public void setTestList(List<Test> testList) {
this.testList = testList;
}
}
Test.java
#XmlRootElement(name = "test")
public class Test {
#XmlElement
private String one;
#XmlElement
private String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
Marshalling with JAXB
//Create a test
Test test = new Test();
test.setOne("1");
test.setTwo("2");
//Create a something
Something something = new Something();
//Add test into the testList of something
something.getTestList().add(test);
JAXBContext jaxbContext = JAXBContext.newInstance(Something.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(something, new File("something.xml"));
something.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Something>
<test>
<one>1</one>
<two>2</two>
</test>
</Something>

I solved it by modifying the xml class that wsimport generated as follows:
// #XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "someName"/*, propOrder = {
"one",
"two"
} */ )
// #XmlRootElement(name = "test")
#XmlAccessorType(XmlAccessType.NONE)
public class MyClass {
// #XmlElement
protected String one;
// #XmlElement
protected String two;
...
// my own custom wrapper
#XmlElement
protected MyClassWrapper test = new MyClassWrapper(this);
}
Where my wrapper looks something like this:
#XmlAccessorType(XmlAccessType.NONE)
#XmlType(name = "someNameWrapper", propOrder = {
"one",
"two"
})
public class MyClassWrapper {
public MyClassWrapper() {}
public MyClassWrapper(MyClass base) {
this.base = base;
}
#XmlElement
public getOne() { return base.getOne(); }
#XmlElement
public getTwo() { return base.getTwo(); }
}

Related

JAXB XMLAdapter: Is there a way to convert this method into JAXB XmlAdapter

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.

How to read xsi:type with java annotations

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]

How to avoid root element annotation while marshalling in JAXB?

I want to store my object data to XML. Below code spinets will show the example of model class.
Class Model
{
#XmlElement
private int id;
#XmlElement
Private string name;
}
I will have multiple model objects which will be stored in some list as below
#XmlRootElement
Class ModelWrapper
{
#XmlElement
#XmlJavaTypeAdapter(value = ListAdapter.class, type = List.class)
List<model> list;
public setlist(List<model>list)
{
//setting list
}
public List<model> getlist()
{
return list
}
}
Now if I marshall this using JAXB, something like this will be produced:
<Modelwrapper>
<List>
......
......
</List>
</Modelwrapper>
I want to avoid one of the root may be list or Modelwrapper.
Is there any way to do it?
In this way your xml will be
<ModelWrapper>
<model>
......
</model>
<model>
......
</model>
</ModelWrapper>
ModelWrapper.java
#XmlRootElement
public class ModelWrapper {
#XmlElement(name = "model")
protected List<Model> list;
Test
ModelWrapper.java
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.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ModelWrapper", propOrder = {
"list"
})
public class ModelWrapper {
#XmlElement(name = "model")
private List<Model> list;
public List<Model> getList() {
return list;
}
public void setList(List<Model> list) {
this.list = list;
}
}
Model.java
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.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Model", propOrder = {
"id","name"
})
public class Model {
#XmlElement
private int id;
#XmlElement
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Main {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(ModelWrapper.class);
ModelWrapper mw = new ModelWrapper();
List<Model> list = new ArrayList<Model>();
Model m = new Model();
m.setId(1);
m.setName("model1");
list.add(m);
m = new Model();
m.setId(1);
m.setName("model2");
list.add(m);
mw.setList(list);
Marshaller mar = jc.createMarshaller();
mar.marshal(mw, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<modelWrapper>
<model>
<id>1</id>
<name>model1</name>
</model>
<model>
<id>1</id>
<name>model2</name>
</model>
</modelWrapper>

EclipseLink MOXy with recursive data structures / child elements of same type

I am using EclipseLink MOXy and have a data structure that has child elements of the same data type. Now I don't want to serialize the datastructure with infinite depth, but only the first level.
Here is some example code of the data structure:
package test;
import java.util.Collection;
import java.util.Vector;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement
public class MyClass {
private int id;
private String details;
private Collection<MyClass> children = new Vector<MyClass>();
public MyClass() {
}
public MyClass(int id, String details) {
this.id = id;
this.details = details;
}
#XmlElementWrapper
#XmlElementRef
public Collection<MyClass> getChildren() {
return children;
}
public void addChild(MyClass child) {
children.add(child);
}
public String getDetails() {
return details;
}
#XmlAttribute
public int getId() {
return id;
}
public void setChildren(Collection<MyClass> children) {
this.children = children;
}
public void setDetails(String details) {
this.details = details;
}
public void setId(int id) {
this.id = id;
}
}
And my test program:
package test;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main(String[] args) throws Exception {
MyClass l1 = new MyClass(1, "Level 1");
MyClass l2 = new MyClass(2, "Level 2");
l1.addChild(l2);
MyClass l3 = new MyClass(3, "Level 3");
l2.addChild(l3);
JAXBContext jc = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(l1, System.out);
}
}
The following XML is generated:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass id="1">
<children>
<myClass id="2">
<children>
<myClass id="3">
<children/>
<details>Level 3</details>
</myClass>
</children>
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
However, I'd like the xml too look like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myClass id="1">
<children>
<myClass id="2">
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
Thanks.
To accomplish this use case we will leverage two concepts from JAXB: XmlAdapter and Marshaller.Listener.
MyClassAdapter
We will leverage the default JAXB behaviour of not marshalling an element for a null value. To do this we will implement an XmlAdapter that returns null after a specified level has been reached. To count the levels we will create a Marshaller.Listener.
package forum11769758;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyClassAdapter extends XmlAdapter<MyClass, MyClass>{
private int levels;
private MyMarshallerListener marshallerListener;
public MyClassAdapter() {
}
public MyClassAdapter(int levels) {
this.levels = levels;
}
public Marshaller.Listener getMarshallerListener() {
if(null == marshallerListener) {
marshallerListener = new MyMarshallerListener();
}
return marshallerListener;
}
#Override
public MyClass marshal(MyClass myClass) throws Exception {
if(null == marshallerListener || marshallerListener.getLevel() < levels) {
return myClass;
}
return null;
}
#Override
public MyClass unmarshal(MyClass myClass) throws Exception {
return myClass;
}
static class MyMarshallerListener extends Marshaller.Listener {
private int level = 0;
public int getLevel() {
return level;
}
#Override
public void afterMarshal(Object object) {
if(object instanceof MyClass) {
level--;
}
}
#Override
public void beforeMarshal(Object object) {
if(object instanceof MyClass) {
level++;
}
}
}
}
MyClass
The #XmlJavaTypeAdapter annotation is used to specify that an XmlAdapter should be used.
package forum11769758;
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement
public class MyClass {
private int id;
private String details;
private Collection<MyClass> children = new Vector<MyClass>();
public MyClass() {
}
public MyClass(int id, String details) {
this.id = id;
this.details = details;
}
#XmlElementWrapper
#XmlElementRef
#XmlJavaTypeAdapter(MyClassAdapter.class)
public Collection<MyClass> getChildren() {
return children;
}
public void addChild(MyClass child) {
children.add(child);
}
public String getDetails() {
return details;
}
#XmlAttribute
public int getId() {
return id;
}
public void setChildren(Collection<MyClass> children) {
this.children = children;
}
public void setDetails(String details) {
this.details = details;
}
public void setId(int id) {
this.id = id;
}
}
Test
Since we need to use the XmlAdapter in a stateful way, we will set an instance of it on the Marshaller, we will also set the instance of Marshaller.Listener we created on the Marshaller.
package forum11769758;
import javax.xml.bind.*;
public class Test {
public static void main(String[] args) throws Exception {
MyClass l1 = new MyClass(1, "Level 1");
MyClass l2 = new MyClass(2, "Level 2");
l1.addChild(l2);
MyClass l3 = new MyClass(3, "Level 3");
l2.addChild(l3);
JAXBContext jc = JAXBContext.newInstance(MyClass.class);
Marshaller marshaller = jc.createMarshaller();
MyClassAdapter myClassAdapter = new MyClassAdapter(2);
marshaller.setAdapter(myClassAdapter);
marshaller.setListener(myClassAdapter.getMarshallerListener());
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(l1, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<myClass id="1">
<children>
<myClass id="2">
<children/>
<details>Level 2</details>
</myClass>
</children>
<details>Level 1</details>
</myClass>
For More Information
The following articles expand on the topics discussed in this answer:
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html

JAXB mappable XML elements

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
}

Categories

Resources