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)
}
Related
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;
}
I have a List<SelectConditionHeaderModel> .
When i am marshalling this list, I am getting an error :
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML
My abstract Parent class.
#XmlRootElement
#XmlSeeAlso({ SelectConditionHeaderModel.class,
SelectConditionModel.class })
public abstract class SelectConditionParentModel {
#XmlInverseReference(mappedBy = "conditionList")
SelectConditionParentModel parent;
public SelectConditionParentModel getParent() {
return parent;
}
public void setParent(HbaseSelectConditionParentModel parent) {
this.parent = parent;
}
}
Header class extending the abstract parent
#XmlRootElement
public class SelectConditionHeaderModel extends
SelectConditionParentModel {
List<SelectConditionParentModel> conditionList;
String header;
public List<SelectConditionParentModel> getConditionList() {
return conditionList;
}
public void setConditionList(List<SelectConditionParentModel> condition) {
this.conditionList = condition;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
Condition class extending the Abstract Parent
#XmlRootElement
public class SelectConditionModel extends SelectConditionParentModel {
String tableName;
public String getTableName() {
return columnFamily;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
Please help me out with this . I have also used XMLInverseReference but it seems that it is not working.
Try to use this configuration based on #XmlID and #XmlIDREF.
or you can put #XmlTransient to exclude the subgraph.
If you are using EclipseLink JAXB (MOXy) as your JAXB (JSR-222) provider then you can leverage our #XmlInverseReference extension to map your bi-directional relationship.
You can find a complete example on my blog:
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html
I have 2 classes, one extends the other. The superclass marshals correctly, but the subclass, which adds one attribute, does not. The extra attribute is not present in the XML.
Superclass:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.NONE)
public class SessionRecord extends Record {
SimpleDateFormat hhmm = new SimpleDateFormat("HH:mm");
SimpleDateFormat day = new SimpleDateFormat("EEEEE");
#XmlAttribute protected int sessionId;
#XmlAttribute protected boolean open;
#XmlAttribute protected boolean night;
protected Date start;
protected Date finish;
protected boolean setup;
protected boolean takedown;
#XmlAttribute
public String getDescription() {
if (start==null) start = new Date();
if (finish==null) finish = new Date();
return day.format(start)+(night ? " Night " : " ")+hhmm.format(start)+"-"+hhmm.format(finish)+" "+type();
}
private String type() {
return setup ? "Setup" : (open ? "Open" : (takedown ? "Takedown" : ""));
}
#XmlAttribute
public boolean isSetupTakedown() {
return setup || takedown;
}
}
This produces XML elements similar to this:
<sessionRecord setupTakedown="true" description="Saturday 09:00-13:00 Setup" night="false" open="false" sessionId="0"/>
which is OK.
But the subclass:
#XmlRootElement()
public class VolunteerSession extends SessionRecord {
#XmlAttribute private boolean available;
}
Produces identical output, the available attribute is not marshalled. Why is JAXB not marshalling the extra attribute?
EDIT
further information:
Record superclass is merely this:
public abstract class Record {}
Here is the class representing the top-level document element. It contains lists of Records:
#XmlRootElement(name="response")
#XmlSeeAlso({
RecordList.class,
VolunteerAssignment.class,
VolunteerRecord.class,
SessionRecord.class,
VolunteerSession.class,
VolunteerArea.class,
PossibleAssignment.class})
public class XMLResponse {
#XmlAttribute private String errorMessage;
private List<RecordList<? extends Record>> recordLists = new ArrayList<RecordList<? extends Record>>();
//snip...
public void setError(String errorMessage) {
this.errorMessage = errorMessage;
}
#XmlMixed
public List<RecordList<? extends Record>> getRecordLists() {
return recordLists;
}
}
and finally, RecordList
#XmlRootElement()
public class RecordList<T extends Record> {
#XmlAttribute private String name;
#XmlAttribute private int total;
#XmlAttribute private int count;
#XmlAttribute private int start;
#XmlAttribute private boolean update;
private List<T> records;
// snip constructors, setters
#XmlMixed
public List<T> getRecords() {
return records;
}
}
It sounds as though the VolunteerSession class is not being included in the JAXBContext. This can happen depending on how you created your JAXBContext. Below is some example code where the same object is marshalled based on 3 different instances of JAXBContext each bootstrapped off a different class.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
VolunteerSession volunteerSession = new VolunteerSession();
marshal(VolunteerSession.class, volunteerSession);
marshal(SessionRecord.class, volunteerSession);
marshal(XMLResponse.class, volunteerSession);
}
private static void marshal(Class bootstrapClass, Object object) throws Exception {
System.out.println(bootstrapClass.getName());
JAXBContext jc = JAXBContext.newInstance(bootstrapClass);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, System.out);
System.out.println();
}
}
Output
When the JAXBContext is bootstrapped off of VolunteerSession obviously it has the necessary information.
When the JAXBContext is bootstraped off of the super class SessionRecord it doesn't pull in VolunteerSession. JAXB will automatically process metadata for super classes, but not subclasses. #XmlSeeAlso is usually used in this case to reference mapped subclasses.
VolunteerRecord contains an #XmlSeeAlso annotation that references VolunteerSession. Therefore VolunteerSession is processed as part of the JAXBContext and contains the necessary information when marshalled.
forum20908213.VolunteerSession
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<volunteerSession available="false" sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
forum20908213.SessionRecord
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sessionRecord sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
forum20908213.XMLResponse
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<volunteerSession available="false" sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
You have to list all of your subclasses in #XmlSeeAlso annotation of your parent class.
I have the following simple jaxB class that takes generic type E
#XmlAccessorType(XmlAccessType.FIELD)
#XmlTransient
#XmlRootElement(name = "searchResponseBase")
public abstract class SearchResponseBase<E>{
#XmlElement(type=NameSearchResults.class)
protected E searchResults;
public E getSearchResults()
{
return searchResults;
}
public void setSearchResults(E mSearchResults)
{
this.searchResults = mSearchResults;
}
}
I need to remove the reference to NameSearchResults #XmlElement(type=NameSearchResults.class) to make the base actually generic, but if I do I get the error.
error
[com.sun.istack.internal.SAXException2: class au.test.nameSearch.NameSearchResults nor any of its super class is known to this context.
javax.xml.bind.JAXBException: class au.test.nameSearch.NameSearchResults nor any of its super class is known to this context.]
This is an example of a class that extends it
extended class
#SuppressWarnings("javadoc")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {
"searchRequest",
"searchResults"
})
#XmlRootElement(name = "searchResponse")
public class SearchResponse extends SearchResponseBase<NameSearchResults> {
#XmlElement(required = true)
protected SearchRequest searchRequest;
public SearchRequest getSearchRequest() {
return searchRequest;
}
public void setSearchRequest(SearchRequest value) {
this.searchRequest = value;
}
}
How do i make the base class actually generic?
preferably i would like my extended class to work in the format SearchResponse<E> extends SearchResponseBase<E> and use it as a generic type too.
if i do as paul suggested i can get teh class to:
#XmlRootElement(name = "searchResponse")
public class SearchResponse<E extends NameSearchResults> extends SearchResponseBase<E> {
#XmlElement(required = true)
protected SearchRequest searchRequest;
protected E searchResults;
public SearchRequest getSearchRequest() {
return searchRequest;
}
public void setSearchRequest(SearchRequest value) {
this.searchRequest = value;
}
#Override
public E getSearchResults() {
return searchResults;
}
#Override
public void setSearchResults(E mSearchResults) {
this.searchResults = mSearchResults;
}
}
is there a way i can push the NameSearchResults out of this <E extends NameSearchResults>?
Thanks to #PaulBellora for the help, the base and extend class will both become abstract then haveing a Name implimentation, like this:
Base
#XmlRootElement(name = "searchResponseBase")
public abstract class SearchResponseBase<E>{
public abstract E getSearchResults();
public abstract void setSearchResults(E mSearchResults);
}
Extended Base
#XmlRootElement(name = "searchResponse")
public abstract class SearchResponse<E> extends SearchResponseBase<E>{
public abstract SearchRequest getSearchRequest();
public abstract void setSearchRequest(SearchRequest value);
}
Name Implimentation
#XmlRootElement(name = "nameSearchResponse")
public class NameSearchResponse extends SearchResponse<NameSearchResults>{
#XmlElement(required = true)
protected SearchRequest searchRequest;
protected NameSearchResults searchResults;
#Override
public NameSearchResults getSearchResults() {
return searchResults;
}
#Override
public void setSearchResults(NameSearchResults mSearchResults) {
this.searchResults = mSearchResults;
}
#Override
public SearchRequest getSearchRequest() {
return searchRequest;
}
#Override
public void setSearchRequest(SearchRequest value) {
this.searchRequest = value;
}
}
I'm unfamiliar with JAXB, but you could try making getSearchResults and setSearchResults abstract methods, and implement them only when E was resolved. For example:
//annotations ommitted
public abstract class SearchResponseBase<E>{
public abstract E getSearchResults();
public abstract void setSearchResults(E mSearchResults);
}
//annotations ommitted
public class SearchResponse extends SearchResponseBase<NameSearchResults> {
#XmlElement(type=NameSearchResults.class)
protected NameSearchResults searchResults;
#Override
public final NameSearchResults getSearchResults() {
return searchResults;
}
#Override
public final void setSearchResults(NameSearchResults mSearchResults) {
this.searchResults = mSearchResults;
}
...
}
I am stuck with the inheritance problem while trying to unmarshal to object. Here is my class
A
#XmlRootElement(name="A")
public abstract class A{
}
B
#XmlRootElement(name="B")
public class B extends A{
String bField;
#XmlAttribute(name="b")
public String getBField(){
return bField;
}
public void setBField(String value){
this.bField = value;
}
}
C
#XmlRootElement(name="C")
public class C extends A{
String cField;
#XmlAttribute(name="c")
public String getCField(){
return cField;
}
public void setCField(String value){
this.cField = value;
}
}
Container
#XmlRootElement(name="container")
public class Container{
ArrayList<B> listB;
ArrayList<C> listC;
public ArrayList<B> getListB(){
return listB;
}
#XmlElementWrapper(name="list-B")
#XmlElement(name="b")
public ArrayList<B> getListB(){
return listB;
}
#XmlElementWrapper(name="list-C")
#XmlElement(name="c")
public ArrayList<C> getListC(){
return listC;
}
public ArrayList<C> getListC(){
return listC;
}
}
Then the input XML file
<container>
<list-B>
<b b="BFied"/>
</list-B>
<list-C>
<c c="CField"/>
</list-C>
</container>
I used EclipseLink JAXB integrated with Spring OXM. When i unmarshal xml file to an instance of Container, every thing is duplicated. In list B i have 2 B instances which duplicated ( the same thing with list C).
Please let me know where did i do wrong? Thank you!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I haven't been able to reproduce the issue that you are seeing. I am using the EclipseLink 2.4.0 which can be obtained from the following location:
http://www.eclipse.org/eclipselink/downloads/
Below is my complete code based on your question:
A
package forum11642669;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="A")
public abstract class A{
}
B
package forum11642669;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "B")
public class B extends A {
String bField;
#XmlAttribute(name = "b")
public String getBField() {
return bField;
}
public void setBField(String value) {
this.bField = value;
}
}
C
package forum11642669;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "C")
public class C extends A {
String cField;
#XmlAttribute(name = "c")
public String getCField() {
return cField;
}
public void setCField(String value) {
this.cField = value;
}
}
Container
The version of the Container class you had in your question wouldn't compile, so I've modified it below:
package forum11642669;
import java.util.ArrayList;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "container")
public class Container {
ArrayList<B> listB;
ArrayList<C> listC;
#XmlElementWrapper(name = "list-B")
#XmlElement(name = "b")
public ArrayList<B> getListB() {
return listB;
}
public void setListB(ArrayList<B> listB) {
this.listB = listB;
}
#XmlElementWrapper(name = "list-C")
#XmlElement(name = "c")
public ArrayList<C> getListC() {
return listC;
}
public void setListC(ArrayList<C> listC) {
this.listC = listC;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum11642669;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Container.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11642669/input.xml");
Container container = (Container) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(container, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<container>
<list-B>
<b b="BFied"/>
</list-B>
<list-C>
<c c="CField"/>
</list-C>
</container>