JAXB how to nest several objects? - java

I've been trying to search how to do this but I haven't found an answer to my exact requirements:
Let's say we had this 3 classes:
public class Main {
public ArrayList<MyFirstClass> myFirstClass;
}
class MyFirstClass {
public int num;
public MySecondClass mySecondClass;
}
class MySecondClass {
public String otherStr;
public MyThirdClass myThirdClass;
}
class MyThirdClass {
public int otherNum;
}
I wanto to be able to read these XML with the unmarshaller:
<Main>
<MyFirstClasses>
<MyFirstClass>
<num>1</num>
<MySecondClass>
<str>Hello</str>
<MyThirdClass>
<otherNum>2</otherNum>
</MyThirdClass>
</MySecondClass>
</MyFirstClass>
<MyFirstClasses>
</Main>
Where I'm basically able to set the variables that are objects (MySecond/Third Class).
I know I can use #XMLRootElement and then #XmlElementWrapper(name="aName") and #XmlElement(name="aName") to do the
<Main>
<MyFirstClasses>
<MyFirstClass>
<num>1</num>
</MyFirstClass>
<MyFirstClasses>
</Main>
But how can I then nest the MySecondClass inside MyFirstClass so I can set it's values, because otherwise the FirstClassObject will have a MySecondClass which has null values.
Thanks in advance!

The problem is that your xml does not match your POJOs. You can use annotations to fix this(renaming fields would also work). Try this:
#XmlRootElement(name = "Main")
public class Main {
#XmlElementWrapper(name = "MyFirstClasses")
#XmlElement(name = "MyFirstClass")
private List<MyFirstClass> myFirstClass;
}
Then FirstClass:
#XmlAccessorType(XmlAccessType.FIELD)
public class MyFirstClass {
private int num;
#XmlElement(name = "MySecondClass")
private MySecondClass mySecondClass;
}
And MySecondClass:
#XmlAccessorType(XmlAccessType.FIELD)
public class MySecondClass {
private String str;
#XmlElement(name = "MyThirdClass")
private MyThirdClass myThirdClass;
}
Finally MyThirdClass:
#XmlAccessorType(XmlAccessType.FIELD)
public class MyThirdClass {
public int otherNum;
}

Related

Jaxb custom element from two fields of an object

Is there a way with jaxb to create a custom element from two fields from an object?
Here's an example of what I have and what I want. I realize I could extract the fields into a seperate annotated class, but I'm curious of theres a way to do something similar to this.
#XmlRootElement()
public class Foo {
public String bar
public String baz
}
Expected output xml:
<foo>
<customElement bar="barValue">bazValue</customElement>
</foo>
Thanks!
class for foo
#XmlRootElement()
public class Foo {
private customElement CustomElement;
public CustomElement getCustomElement(){
return customElement;
}
#XmlElement
public void setCustomElement(CustomElement customElement){
this.customElement = customElement;
}
}
class for custom element
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomElement {
#XmlAttribute
private String bar;
#XmlValue
private String baz
// set getters and setters
}

JAXB unmarshal child attributes without creation of child class

I want to unmarshal a (simplified) XML structure like this:
<parent>
<a>AValue</a>
<b>BValue</b>
<c someAttribute = "true">CValue</c>
</parent>
I know how to do this with declaring a class C like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "c", propOrder = {
"someAttribute"
})
public class C{
#XmlValue
private String c;
#XmlAttribute ( name="someAttribute")
private boolean someAttribute;
//getters and setters
}
And getting it as a member in class parent like this:
public class Parent{
private String a;
private String b;
private C c;
//getters and setters for c,b,a
}
This works finde and i can access the value of C via parent.getC().getC();
My Question is how to achieve that i do not have to create a class C and get the value and attribute of C as a member of parent, without editing the parent Pojo with new members and other getters and setters.
I already tried to do this via Listeners and searched for similar structures, but i haven't got any ideas left.
I finally figured out how to achieve this.
Its necessary to use the #XmlJavaTypeAdapter Annotation and mark the C class as an #XmlRootElement as well as an #XmlAccessorType(XmlAccessType.FIELD).
Furthermore one need to use the #XmlTransient on the getter of the String member which was annotated with #XmlJavaTypeAdapter.
Full solution:
Class C:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class C{
#XmlValue
private String c;
#XmlAttribute
private boolean someAttribute;
//getters and setters for both
Class Adapter:
public class Adapter extends XmlAdapter<C, String> {
public String unmarshal(C pC) throws Exception {
//some possible handling with the attribute over pC.getSomeAttribute();
return pC.getC();
}
public C marshal(String pC) throws Exception {
C c = new C();
c.setC(pC)
//some possible handling to set the attribute to c
return c;
}
Class Parent:
public class Parent{
private String a;
private String b;
#XmlJavaTypeAdapter(Adapter.class)
private String c;
#XmlTransient
public String getC() {
return c;
}
//getters and setters for b,a and setter for C
}

XML element Annotation in Java

I have question about how to add additional attributes to a root tag of an xml-structure.
let say this is my java-class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "myRoot")
#XmlType(name = "myType")
public class A implements B {
#CoId
private int id;
public int getId() {
return id;
}
}
and I want as result an output which can be like this below:
"<myRoot newAttribute="something">
<id>111</id>
</myRoot>"
I mean the attribute newAttribute should be dynamically added. What is the annotation for this, how should I adapt my class here?

JAXB abstract class with #XmlTransient

I'm using JAXB to unmarshal some xml into an object(s).
I have a class which inherit from an abstract class. I've marked the abstract class as #XmlTransient. Then using XMLType PropOrder I can access the properties in the abstract class like so:
#XmlType( propOrder = { "id"...
Cool. Problem is sometimes it isn't an element that I want to access but rather an attribute. Normally you would define such a property using #XMLAttribute to indicate the value is stored in an xml attribute and not an element. But given the fact that I've already used XMLTransient on the abstract class where 'id' is defined, JAXB complains when I try to mark the field as #XMLAttribute.
JAXB is complaining that I'm trying to access/return two fields of with the same name.
Can anyone please point me in the right direction? I'm building for GAE so I dn't really want to use any other libraries.
Thanks in advance!
Below are a couple of things you can do:
Java Model
Foo
You can annotate the property on the parent class with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlTransient
public class Foo {
private String att1;
private String att2;
#XmlAttribute
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
}
Bar
You can override the property on the subclass and annotate it with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Bar extends Foo {
#Override
#XmlAttribute
public String getAtt2() {
return super.getAtt2();
}
#Override
public void setAtt2(String att2) {
super.setAtt2(att2);
}
}
Demo Code
Demo
Here is some demo code you can run to show that everything works.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Bar.class);
Bar bar = new Bar();
bar.setAtt1("a");
bar.setAtt2("b");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(bar, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<bar att1="a" att2="b"/>

Unexpected element (uri:"", local: "element"). Expected elements are <{}link> <{}size>

When trying to unmarshall this xml:
<holder>
<name>a</name>
<elements>
<element>
<name>elem</name>
</element>
</elements>
</holder>
I get the error unexpected element (uri:"", local:"element"). Expected elements are <{}link>,<{}totalSize> in the ValidationEventHandler and the tag <elements> (and therefore the elements field in Holder class) is ignored.
When generating the XML both link and totalSize are not outputted as they are nil.
JAVA MODEL
The hierarchy is a bit complex:
(Simplified for the sake of the question)
ElementRoot
abstract ElementRoot has the link member
public abstract class ElementRoot implements Serializable {
protected String link;
#XmlElement(name = "link")
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}
Wrapper
abstract Wrapper extends ElementRoot and has the totalSize member
public abstract class Wrapper<T> extends ElementRoot {
protected int totalSize;
protected List<T> collection = new ArrayList<>();
#XmlElement
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public abstract List<T> getCollection();
}
Holder
Holder extends ElementRoot
#XmlRootElement(name = "holder")
#XmlType(propOrder = {"name", "elements"})
public class Holder extends ElementRoot {
private String name;
private Elements elements;
// setters and getters not annotated
}
Elements
Elements extends Wrapper and has a collection of Element
import java.util.Collection;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "elements)
public class Elements extends Wrapper {
#Override
#XmlElement(name="element")
public Collection<Element> getElements() {
return elements;
}
// No setter, to add getElements().add(element)
}
Element
Element extends ElementRoot
#XmlRootElement(name = "element")
#XmlType(propOrder = {"id", "name"})
public class Element extends ElementRoot {
private Integer id;
private String name;
// setters and getters no annotated
}
ENVIRONMENT
I'm using java 7:
JAXB-api 2.2.7
MOXy 2.5.0
There appears to be a bug in EclipseLink JAXB (MOXy) for this use case related to the abstract getCollecion property. We have opened up the following bug that you can use to track our progress on this issue:
http://bugs.eclipse.org/411408
WORK AROUND
Wrapper
We can use #XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties will be processed (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.NONE)
public abstract class Wrapper<T> extends ElementRoot {
protected int totalSize;
protected List<T> collection = new ArrayList<>();
#XmlElement
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public abstract List<T> getCollection();
}
Elements
Since #XmlAccessorType is inherited by the subclasses we will specify XmlAccessType.PUBLIC to return things to normal. Note: I assume the getElements() method in your question should have been getCollection().
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "elements")
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class Elements extends Wrapper {
#Override
#XmlElement(name="element")
public List<Element> getCollection() {
return collection;
}
// No setter, to add getElements().add(element)
}

Categories