this is my xml structure
<catalog>
<course>
<course_id></course_id>
<subjects>
<subject>
<subject_id></subject_id>
</subject>
<subject>
<subject_id></subject_id>
</subject>
</subjects>
</course>
</catalog>
So, i'v tried to bind this xml to a class using JAXB Unmarshalling, but the result was nothing.
I was thinking, i have 2 base elements, course and subject, so i built 2 classes based on these elements.
This to control the course tag
#XmlRootElement(name="catalog")
#XmlAccessorType(XmlAccessType.FIELD)
public class curso {
#XmlElement(name="course_id")
int course_id;
#XmlElementWrapper(name="subjects")
#XmlElement(name="subject")
List <subject> subjects = new ArrayList<>();
public void setCourse_id(int curso_id) {
this.curso_id = curso_id;
}
public void setSubjects(List<subject> subjects) {
this.subjects = subjects;
}
}
And This to control the subject tag.
public class subject {
String subject_id;
#XmlElement(name="subject_id")
public void setSubjectId(String id) {
this.subject_id = id;
}
}
I made some to string functions, and my output was nothing.
What is the problem?
The course element also have a wrapper tag <course> So either you need to change you xml to remove <catalog> tag and make <course> as the root. Or you should create a new class catalog and make course as a field. Like
#XmlRootElement(name="catalog")
#XmlAccessorType(XmlAccessType.FIELD)
class catalog {
#XmlElement(name="course")
curso course;
public curso getCourse() {
return course;
}
public void setCourse(curso course) {
this.course = course;
}
#Override
public String toString() {
return "catalog [course=" + course + "]";
}
}
A complete example
import java.io.File;
import java.util.ArrayList;
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.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="catalog")
#XmlAccessorType(XmlAccessType.FIELD)
class catalog {
#XmlElement(name="course")
curso course;
public curso getCourse() {
return course;
}
public void setCourse(curso course) {
this.course = course;
}
#Override
public String toString() {
return "catalog [course=" + course + "]";
}
}
class curso {
#XmlElement(name = "course_id")
int course_id;
#XmlElementWrapper(name = "subjects")
#XmlElement(name = "subject")
List<subject> subjects = new ArrayList<>();
public void setCourse_id(int curso_id) {
this.course_id = curso_id;
}
public void setSubjects(List<subject> subjects) {
this.subjects = subjects;
}
#Override
public String toString() {
return "curso [course_id=" + course_id + ", subjects=" + subjects + "]";
}
}
class subject {
String subject_id;
#XmlElement(name = "subject_id")
public void setSubjectId(String id) {
this.subject_id = id;
}
#Override
public String toString() {
return "subject [subject_id=" + subject_id + "]";
}
}
public class JaxbExample2 {
public static void main(String[] args) {
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(catalog.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
catalog customer = (catalog) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Related
I have very interesting xml and i am try to convert this xml to pojo. after my jaxb unmarshalling its return empty object.
here is xml:
<ServiceResultOfArrayOfstringuHEDJ7Dj xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/HotelWeb.SanAdminSite.RestWebApi.App_Data">
<Data xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:string>1105</d2p1:string>
<d2p1:string>1111</d2p1:string>
</Data>
<Error>
<Code></Code>
<Message></Message>
</Error>
<InfoMessage></InfoMessage>
<IsSuccessful>true</IsSuccessful>
</ServiceResultOfArrayOfstringuHEDJ7Dj>
My POJO's :
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)
#XmlRootElement(name = "ServiceResultOfArrayOfstringuHEDJ7Dj", namespace = "http://schemas.datacontract.org/2004/07/HotelWeb.SanAdminSite.RestWebApi.App_Data")
public class PostSendLogIdServiceXmlResult {
private String xmlns;
#XmlElement(name = "InfoMessage")
private String infoMessage;
#XmlElement(name = "Error")
private Error error;
#XmlElement(name = "Data")
private PostSendLogIdData data;
#XmlElement(name = "IsSuccessful")
private boolean isSuccessful;
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
this.xmlns = xmlns;
}
public String getInfoMessage() {
return infoMessage;
}
public void setInfoMessage(String infoMessage) {
this.infoMessage = infoMessage;
}
public Error getError() {
return error;
}
public void setError(Error error) {
this.error = error;
}
public PostSendLogIdData getData() {
return data;
}
public void setData(PostSendLogIdData data) {
this.data = data;
}
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
#Override
public String toString() {
return "PostSendLogIdServiceXmlResult{" +
"xmlns='" + xmlns + '\'' +
", infoMessage='" + infoMessage + '\'' +
", error=" + error +
", data=" + data +
", isSuccessful=" + isSuccessful +
'}';
}
}
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 = "Error")
#XmlAccessorType(XmlAccessType.FIELD)
public class PostSendLogIdError {
#XmlElement(name = "Code")
private String code;
#XmlElement(name = "Message")
private String message;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
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 java.util.Arrays;
#XmlRootElement(name = "Data", namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")
#XmlAccessorType(XmlAccessType.FIELD)
public class PostSendLogIdData {
#XmlElement(name = "string")
private String[] postSendLogIds;
private String _xmlns;
public String[] getPostSendLogIds() {
return postSendLogIds;
}
public void setPostSendLogIds(String[] postSendLogIds) {
this.postSendLogIds = postSendLogIds;
}
public String get_xmlns() {
return _xmlns;
}
public void set_xmlns(String _xmlns) {
this._xmlns = _xmlns;
}
#Override
public String toString() {
return "PostSendLogIdData{" +
"postSendLogIds=" + Arrays.toString(postSendLogIds) +
", _xmlns='" + _xmlns + '\'' +
'}';
}
}
And My Jaxb converting implementation:
context = JAXBContext.newInstance(PostSendLogIdServiceXmlResult.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
PostSendLogIdServiceXmlResult unmarshal = (PostSendLogIdServiceXmlResult) unmarshaller.unmarshal(new StringReader(response));
After this I'm getting new PostSendLogIdServiceXmlResult object with empty values.
Can you give me any advice.
Thanks in Advance.
Look at the XML again. The namespace is not applied to the <Data> element, it is applied to the <string> sub-elements. Notice where the d2p1: prefix is applied.
Also, the <Data> is not a root element, so the entire #XmlRootElement(name = "Data", ... annotation is unused, i.e. unnecessary, and it seems to be confusing you, so I recommend removing the annotation from the PostSendLogIdData class.
Correct use is:
#XmlElement(name = "string", namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")
private String[] postSendLogIds;
I am trying to unmarshal XML to java class as follows:-
My xml file as follows:-
<Features_res>
<StartWeek>202017</StartWeek>
<EndWeek>202035</EndWeek>
<Pno12>ABCDEEB</Pno12>
<FeatureList>
<Feature>
<Code>T002</Code>
</Feature>
<Feature>
<Code>T002</Code>
</Feature>
</FeatureList>
</Features_res>
Java InteriorResponse:-
#XmlRootElement(name = "Features_res")
public class InteriorResponse {
#XmlElement(name = "StartWeek")
private int sWeek;
#XmlElement(name = "EndWeek")
private int eWeek;
#XmlElement(name = "Pno12")
private String p12;
List<Feature> featureList;
public InteriorResponse() {
}
public InteriorResponse(int startWeek, int endWeek, String pno12) {
super();
this.sWeek = startWeek;
this.eWeek = endWeek;
this.p12 = pno12;
}
public int getStartWeek() {
return sWeek;
}
public void setStartWeek(int startWeek) {
this.sWeek = startWeek;
}
public int getEndWeek() {
return eWeek;
}
public void setEndWeek(int endWeek) {
this.eWeek = endWeek;
}
public String getPno12() {
return p12;
}
public void setPno12(String pno12) {
this.p12 = pno12;
}
public List<Feature> getFeatureList() {
return featureList;
}
#XmlElement(name = "FeatureList")
public void setFeatureList(List<Feature> featureList) {
this.featureList = featureList;
}
}
Another Java Feature:-
#XmlRootElement(name = "Feature")
public class Feature {
//#XmlElement(name = "Feature")
private String feature_;
#XmlElement(name = "code")
private String code_;
public String getCode() {
return code_;
}
public void setCode(String code) {
this.code_ = code;
}
public String getFeature_() {
return feature_;
}
public void setFeature_(String feature_) {
this.feature_ = feature_;
}
}
I am using above class as :-
public static void xmlToInterior() {
File file = new File("minxml.xml");
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(InteriorResponse.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InteriorResponse interiorFeatures = (InteriorResponse) unmarshaller.unmarshal(file);
List<Feature> list_feat = interiorFeatures.getFeatureList();
for(Feature ft : list_feat) {
System.out.println(ft.getCode());
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
Output I have as:-
list_feat
code_ null
feature_ null
And list_feat has size 1. It should 2.
Also I have to name class member sWeek instead of startWeek. Otherwise, jaxbContext = JAXBContext.newInstance(InteriorResponse.class) throws exception like 2 element exist with same name.
XML additional part
I thought I could do the reaming part of the XML. I was trying part by part. But I couldn't do it. I really need to find some good tutorial or book about JXB. All I find a short overview of JXB. Any suggestion about where to read in details?
<Features_res>
<StartWeek>202017</StartWeek>
<EndWeek>202035</EndWeek>
<Pno12>ABCDEEB</Pno12>
<FeatureList>
<Feature>
<Code>T002</Code>
</Feature>
<Feature>
<Code>T002</Code>
</Feature>
</FeatureList>
<OptionList>
<Option>001048</Option>
<Option>000050</Option>
<Option>000790</Option>
</OptionList>
</Features_res>
So I made new class as:-
public class OptionList {
private List<Option> options;
#XmlElement(name = "Option")
public List<Option> getOptions() {
return options;
}
public void setOptions(List<Option> options) {
this.options = options;
}
}
Another class as :-
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
public class Option {
#XmlElement(name = "Option")
private String option_;
public String getOption() {
return option_;
}
public void setOption(String option) {
this.option_ = option;
}
}
And updated for InteriorResponse class as:-
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Features_res")
public class InteriorResponse {
#XmlElement(name = "StartWeek")
private int sWeek;
#XmlElement(name = "EndWeek")
private int eWeek;
#XmlElement(name = "Pno12")
private String p12;
private FeatureList featureList;
private OptionList optionList;
public InteriorResponse() {}
public InteriorResponse(int startWeek, int endWeek, String pno12) {
super();
this.sWeek = startWeek;
this.eWeek = endWeek;
this.p12 = pno12;
}
public int getStartWeek() {
return sWeek;
}
public void setStartWeek(int startWeek) {
this.sWeek = startWeek;
}
public int getEndWeek() {
return eWeek;
}
public void setEndWeek(int endWeek) {
this.eWeek = endWeek;
}
public String getPno12() {
return p12;
}
public void setPno12(String pno12) {
this.p12 = pno12;
}
#XmlElement(name = "FeatureList")
public FeatureList getFeatureList() {
return featureList;
}
public void setFeatureList(FeatureList featureList) {
this.featureList = featureList;
}
#XmlElement(name = "OptionList")
public OptionList getOptionList() {
return optionList;
}
public void setOptionList(OptionList optionList) {
this.optionList = optionList;
}
#Override
public String toString() {
return "InteriorResponse{"
+ "sWeek="
+ sWeek
+ ", eWeek="
+ eWeek
+ ", p12='"
+ p12
+ '\''
+ ", featureList="
+ featureList
+ '}';
}
}
I couldn't get the Option.
option null
The total xml is really huge. I still would like to try the remaing parts by myself part by part.
You need to design your classes according to XML structure. Find below the classes.
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Features_res")
public class InteriorResponse {
#XmlElement(name = "StartWeek")
private int sWeek;
#XmlElement(name = "EndWeek")
private int eWeek;
#XmlElement(name = "Pno12")
private String p12;
private FeatureList featureList;
private OptionList optionList;
public InteriorResponse() {}
public InteriorResponse(int startWeek, int endWeek, String pno12) {
super();
this.sWeek = startWeek;
this.eWeek = endWeek;
this.p12 = pno12;
}
public int getStartWeek() {
return sWeek;
}
public void setStartWeek(int startWeek) {
this.sWeek = startWeek;
}
public int getEndWeek() {
return eWeek;
}
public void setEndWeek(int endWeek) {
this.eWeek = endWeek;
}
public String getPno12() {
return p12;
}
public void setPno12(String pno12) {
this.p12 = pno12;
}
#XmlElement(name = "FeatureList")
public FeatureList getFeatureList() {
return featureList;
}
public void setFeatureList(FeatureList featureList) {
this.featureList = featureList;
}
#XmlElement(name = "OptionList")
public OptionList getOptionList() {
return optionList;
}
public void setOptionList(OptionList optionList) {
this.optionList = optionList;
}
#Override
public String toString() {
return "InteriorResponse{"
+ "sWeek="
+ sWeek
+ ", eWeek="
+ eWeek
+ ", p12='"
+ p12
+ '\''
+ ", featureList="
+ featureList
+ ", optionList="
+ optionList
+ '}';
}
}
Class for Feature object
import javax.xml.bind.annotation.XmlElement;
public class Feature {
#XmlElement(name = "Code")
private String code_;
public String getCode() {
return code_;
}
public void setCode(String code) {
this.code_ = code;
}
#Override
public String toString() {
return "Feature{" + "code_='" + code_ + '\'' + '}';
}
}
Class for FeatureList
import javax.xml.bind.annotation.XmlElement;
import java.util.List;
public class FeatureList {
private List<Feature> features;
#XmlElement(name = "Feature")
public List<Feature> getFeatures() {
return features;
}
public void setFeatures(List<Feature> features) {
this.features = features;
}
}
Class for OptionList
import javax.xml.bind.annotation.XmlElement;
import java.util.List;
public class OptionList {
private List<String> option;
#XmlElement(name = "Option")
public List<String> getOption() {
return option;
}
public void setOption(List<String> option) {
this.option = option;
}
}
For testing and simplicity, I have written a small java test program below.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.util.List;
public class Test {
public static void main(String[] args) {
File file =
new File("E:\\so\\xml\\minxml.xml");
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(InteriorResponse.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InteriorResponse interiorFeatures = (InteriorResponse) unmarshaller.unmarshal(file);
List<Feature> list_feat = interiorFeatures.getFeatureList().getFeatures();
List<String> optionList = interiorFeatures.getOptionList().getOption();
for (String option : optionList) {
System.out.println("Option Value : " + option);
}
for (Feature ft : list_feat) {
System.out.println(ft.getCode());
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
I also provide the sample xml structure below.
<Features_res>
<StartWeek>202017</StartWeek>
<EndWeek>202035</EndWeek>
<Pno12>ABCDEEB</Pno12>
<FeatureList>
<Feature>
<Code>T002</Code>
</Feature>
<Feature>
<Code>T002</Code>
</Feature>
</FeatureList>
<OptionList>
<Option>001048</Option>
<Option>000050</Option>
<Option>000790</Option>
</OptionList>
</Features_res>
I knew very little about JAXB stuff. I have learned a lot from Sambit who helped a lot and gave me the way to start with this JAXB. Later I have implemented my version with less number of Java class and more smart use of JAXB annotations.
My version of InteriorResponse class:-
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
#XmlRootElement(name = "Features_res")
public class InteriorResponse {
#XmlElement(name = "StartWeek")
private int startWeek;
#XmlElement(name = "EndWeek")
private int endWeek;
#XmlElement(name = "Pno12")
private String pno12;
#XmlElementWrapper(name = "FeatureList")
#XmlElement(name = "Feature")
private List<Feature> featureList;
#XmlElementWrapper(name = "OptionList")
#XmlElement(name = "Option")
private List<String> optionList;
public InteriorResponse() {
}
public InteriorResponse(int startWeek, int endWeek, String pno12) {
super();
this.startWeek = startWeek;
this.endWeek = endWeek;
this.pno12 = pno12;
}
#XmlTransient
public int getStartWeek() {
return startWeek;
}
public void setStartWeek(int startWeek) {
this.startWeek = startWeek;
}
#XmlTransient
public int getEndWeek() {
return endWeek;
}
public void setEndWeek(int endWeek) {
this.endWeek = endWeek;
}
#XmlTransient
public String getPno12() {
return pno12;
}
public void setPno12(String pno12) {
this.pno12 = pno12;
}
#XmlTransient
public List<Feature> getFeatureList() {
return featureList;
}
public void setFeatureList(List<Feature> featureList) {
this.featureList = featureList;
}
#XmlTransient
public List<String> getOptionList() {
return optionList;
}
public void setOptionList(List<String> optionList) {
this.optionList = optionList;
}
#Override
public String toString() {
return "InteriorResponse{" + "sWeek=" + startWeek + ", eWeek=" + endWeek + ", pno12='" + pno12 + '\'' + ", featureList=" + featureList + ", optionList="
+ optionList + '}';
}
}
My version of Feature class:-
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
public class Feature {
#XmlElement(name = "Code")
private String code;
#XmlTransient
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
#Override
public String toString() {
return "Feature{" + "code_='" + code + '\'' + '}';
}
}
Note that I don't need any extra wrapper class for FeatuerList and OptionList. It can be done by the JAXB annotation #XmlElementWrapper(name = "FeatureList"). Also, a very important lesson learned. We have to mark all the property's getter method as #XmlTransient. Otherwise, JAXB throws an exception 2 properties found with the same name. Because our class all properties is visible to the JAXB. So we have to mark one as #XmlTransient.
In my opion, it is a better solution than the accepted answer. I gave all the credit to Sambit. I hove this will help others.
I'm trying to create a XML file from a HashMap. For each key of the hash i want an XML file. The value of the key is an ArrayList of Objects. I am using JAXB but the XML files are not created, as the output is not XML valid.
The object class:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Product")
public class Product implements Comparable<Product>{
String ID,description, gtin;
double price;
String date;
Product()
{
}
public String toString()
{
return ID+" "+description+" "+gtin+" "+price+" "+date;
}
public String getID() {
return ID;
}
#XmlElement
public void setID(String ID) {
this.ID = ID;
}
public String getDescription() {
return description;
}
#XmlElement
public void setDescription(String description) {
this.description = description;
}
public String getGtin() {
return gtin;
}
#XmlElement
public void setGtin(String gtin) {
this.gtin = gtin;
}
public double getPrice() {
return price;
}
#XmlElement
public void setPrice(Double price) {
this.price = price;
}
}
The class where i try to create the XMLs:
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class CreateXML {
static void create(HashMap<String, ArrayList<Product> > map) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(ProdsList.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Set setOfKeys = map.keySet();
Iterator iterator = setOfKeys.iterator();
while (iterator.hasNext()) {
String keys = (String) iterator.next();
String filename= "C:\\Users\\As\\Desktop\\Sups\\"+keys+22+".xml";
File file = new File(filename);
ArrayList<Product> value = map.get(keys);
jaxbMarshaller.marshal(value, file);
jaxbMarshaller.marshal(value, System.out);
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
The class for the root of the xml:
import java.util.*;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
//#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="Products")
//#XmlSeeAlso({ArrayList.class})
class ProdsList {
#XmlElement(name="Product")
ArrayList<Product> prods;
public ProdsList(){
prods=new ArrayList<Product>();
}
public ArrayList<Product> getProducts() {
return prods;
}
public void setProducts(ArrayList<Product> prods) {
this.prods = prods;
}
}
How can i fix this. Thanks in advance.
You need to marshal an instance of ProdsList. Instead you are trying to marshall
an ArrayList of Products.
Change
jaxbMarshaller.marshal(value, file);
jaxbMarshaller.marshal(value, System.out);
To
jaxbMarshaller.marshal(new ProdsList(value), file);
jaxbMarshaller.marshal(new ProdsList(value), System.out);
I have found some examples of JAXB2 #XmlRegistry over the internet but no good in-depth tutorials that talk about the concept of using #XmlRegistry with #XmlElementDecl, wonder if its a concept not much explored in general.
Anyways here is my question, first some sample classes that I am using to unmarshall an xml using JAXB:
The main class I am trying to unmarshal using JAXB - Employee.java
package com.test.jaxb;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import com.test.jaxb.dto.Address;
#XmlRootElement
public class Employee {
private int id;
private String name;
private String email;
private List<Address> addresses;
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
#SuppressWarnings("unused")
#XmlRegistry
public static class XMLObjectFactory {
#XmlElementDecl(scope = Employee.class, name= "id")
JAXBElement<String> createEmployeeId(String value) {
return new JAXBElement<String>(new QName("id"), String.class, "100");
}
#XmlElementDecl(scope = Employee.class, name= "name")
JAXBElement<String> createName(String value) {
return new JAXBElement<String>(new QName("name"), String.class, "Fake Name");
}
#XmlElementDecl(scope = Employee.class, name= "email")
JAXBElement<String> createEmail(String value) {
return new JAXBElement<String>(new QName("email"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addresses")
JAXBElement<List> createAddresses(List value) {
return new JAXBElement<List>(new QName("addresses"), List.class, value);
}
}
}
The child class - Address.java
package com.test.jaxb.dto;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import com.test.jaxb.Employee;
#XmlRootElement
public class Address {
private String addressLine1;
private String addressLine2;
private String addressLine3;
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getAddressLine3() {
return addressLine3;
}
public void setAddressLine3(String addressLine3) {
this.addressLine3 = addressLine3;
}
#SuppressWarnings("unused")
#XmlRegistry
private static class XMLObjectFactory {
#XmlElementDecl(scope = Employee.class, name= "addressLine1")
JAXBElement<String> createAddressLine1(String value) {
return new JAXBElement<String>(new QName("addressLine1"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addressLine2")
JAXBElement<String> createAddressLine2(String value) {
return new JAXBElement<String>(new QName("addressLine2"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addressLine3")
JAXBElement<String> createAddressLine3(String value) {
return new JAXBElement<String>(new QName("addressLine3"), String.class, value);
}
}
}
The xml to be unmarshalled - employee.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<name>Vaishali</name>
<email>Vaishali#example.com</email>
<addresses>
<address>
<addressLine1>300</addressLine1>
<addressLine2>Mumbai</addressLine2>
<addressLine3>India</addressLine3>
</address>
<address>
<addressLine1>301</addressLine1>
<addressLine2>Pune</addressLine2>
<addressLine3>India</addressLine3>
</address>
</addresses>
</employee>
Unmarshalling Code :
package com.test.jaxb;
import java.io.FileReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("resources/employee.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
System.out.println(obj);
}
}
When I unmarshal the employee xml using above code, the address list does not get populated. The resulting employee object only has a blank list of adresses. Is there anything wrong with my mappings?
To find out what is going on and see if the employee objects are actually being created using the Object Factory (having the #XMLRegistry annotation), I changed the value of id and name in factory methods, however that had no effect in the output, which tells me JAXB is not actually using the ObjectFactory, why?
Am I going aboout this all wrong? Any help would be appreciated.
#XmlRegistry - how does it work?
#XmlRegistry is used to mark a class that has #XmlElementDecl annotations. To have your JAXB implementation process this class you need to ensure that it is included in the list of classes used to bootstrap the JAXBContext. It is not enough for it to be a static inner class of one of your domain model classes:
JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class);
#XmlElementDecl - how does it work?
If the value of the field/property is going to be a JAXBElement then you need to leverage #XmlElementDecl. A JAXBElement captures information that is can be useful:
Element name, this is necessary if you are mapping to a choice structure where multiple elements are of the same type. If the element name does not correspond to a unique type then you would not be able to round-trip the document.
JAXBElement can be used to represent an element with xsi:nil="true".
XmlObjectFactory
#XmlElementDecl also allows you to specify a scope. I have modified the model from you post a bit. I have introduced an XmlObjectFactory class that has two #XmlElementDecl. Both specify a name of address. I have leveraged the scope property so that for properties within the Employee class the #XmlElementDecl corresponding to the Address class with be used.
package forum11078850;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class XmlObjectFactory {
#XmlElementDecl(scope = Employee.class, name = "address")
JAXBElement<Address> createAddress(Address value) {
return new JAXBElement<Address>(new QName("address"), Address.class, value);
}
#XmlElementDecl(name = "address")
JAXBElement<String> createStringAddress(String value) {
return new JAXBElement<String>(new QName("address"), String.class, value);
}
}
Employee
The #XmlElementRef annotation will cause the value of the property to be matched on its root element name. Possible matches will include classes mapped with #XmlRootElement or #XmlElementDecl.
package forum11078850;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
private int id;
private String name;
private String email;
private List<JAXBElement<Address>> addresses;
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#XmlElementWrapper
#XmlElementRef(name="address")
public List<JAXBElement<Address>> getAddresses() {
return addresses;
}
public void setAddresses(List<JAXBElement<Address>> addresses) {
this.addresses = addresses;
}
}
ObjectFactoryTest
package forum11078850;
import java.io.FileReader;
import javax.xml.bind.*;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("src/forum11078850/input.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class, XmlObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(obj, System.out);
}
}
The Address class and input.xml from my original answer can be used to run this example.
ORIGINAL ANSWER
I'm not sure how you are attempting to use #XmlRegistry, so I will focus on the following part of your post:
When I unmarshal the employee xml using above code, the address list
does not get populated. The resulting employee object only has a blank
list of adresses. Is there anything wrong with my mappings?
Your list of Address objects is wrapped in a grouping element (addresses), so you need to use the #XmlElementWrapper annotation to map this use case. Below is a complete example:
Employee
package forum11078850;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
private int id;
private String name;
private String email;
private List<Address> addresses;
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;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#XmlElementWrapper
#XmlElement(name = "address")
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
}
Address
package forum11078850;
public class Address {
private String addressLine1;
private String addressLine2;
private String addressLine3;
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getAddressLine3() {
return addressLine3;
}
public void setAddressLine3(String addressLine3) {
this.addressLine3 = addressLine3;
}
}
ObjectFactoryTest
package forum11078850;
import java.io.FileReader;
import javax.xml.bind.*;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("src/forum11078850/input.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(obj, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<name>Vaishali</name>
<email>Vaishali#example.com</email>
<addresses>
<address>
<addressLine1>300</addressLine1>
<addressLine2>Mumbai</addressLine2>
<addressLine3>India</addressLine3>
</address>
<address>
<addressLine1>301</addressLine1>
<addressLine2>Pune</addressLine2>
<addressLine3>India</addressLine3>
</address>
</addresses>
</employee>
You have to take a List object of Address. In that object, you will have to add the object which contains data like addressline1. addressline2 and so on.
i.e.
List addrObjList = new List();
addrObjList.add(object); // Bind an object containing data and add one by one
This is my Parser class
public class Test {
public static void main(String args[]) throws Exception {
File file = new File("D:\\Test.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(MyOrder.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
MyOrder customer = (MyOrder) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer.getOrder().getSide());
}
}
This is MyOrder.java file
#XmlRootElement(name = "BXML")
public class MyOrder {
#XmlElement(name = "Bag")
protected Order order;
public MyOrder() {
}
#XmlAttribute
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
This is my Domain Object (Order.java )
#XmlRootElement(name = "BXML")
public class Order {
public Order() {
}
#XmlAttribute(name = "Side")
protected BigInteger Side;
#XmlValue
public BigInteger getSide() {
return Side;
}
public void setSide(BigInteger side) {
Side = side;
}
}
This is the exception that I am getting when I tried to run the program
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
#XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML.
this problem is related to the following location:
at public com.Order com.MyOrder.getOrder()
at com.MyOrder
Class has two properties of the same name "order"
this problem is related to the following location:
at public com.Order com.MyOrder.getOrder()
at com.MyOrder
this problem is related to the following location:
at protected com.Order com.MyOrder.order
at com.MyOrder
For the #XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML. issue you need to change your initialization of JAXBContext to the following:
JAXBContext jaxbContext = JAXBContext.newInstance(MyOrder.class, Order.class);
For the Class has two properties of the same name "order" issue, you need to change the definition of protected Order order; to private Order order;.
Also, you want to change the #XmlRootElement(name = "BXML") of your Order class to #XmlRootElement(name = "Order").
You can see the below sample code to generate Java Object from given XML.It is working fine in my system.
customer.xml
<?xml version="1.0" encoding="UTF-8"?>
<company>
<customer id="100">
<age>25</age>
<name>Ram</name>
<Address>
<city>Bangalore</city>
<country>India</country>
</Address>
<Address>
<city>Patna</city>
<country>India</country>
</Address>
</customer>
<customer id="200">
<age>26</age>
<name>Ashu</name>
<Address>
<city>Delhi</city>
<country>India</country>
</Address>
<Address>
<city>Madhubani</city>
<country>India</country>
</Address>
</customer>
</company>
Company.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;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="company")
public class Company {
#XmlElement(name="customer")
private List<Costumer> custList;
//
public List<Costumer> getCustList() {
return custList;
}
public void setCustList(List<Costumer> custList) {
this.custList = custList;
}
//
#Override
public String toString() {
return "Company [custList=" + custList + "]";
}
}
Costumer.java
#XmlAccessorType(XmlAccessType.FIELD)
class Costumer {
#XmlElement(name="name")
private String name;
#XmlElement(name="age")
private int age;
#XmlElement(name="id")
private int id;
#XmlElement(name="Address")
private List<Address> addressList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
#Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + ", id=" + id + ", addressList=" + addressList + "]";
}
}
Address.java
#XmlAccessorType(XmlAccessType.FIELD)
class Address {
#XmlElement(name="city")
private String city;
#XmlElement(name="country")
private String country;
//
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
//
#Override
public String toString() {
return "Address [city=" + city + ", country=" + country + "]";
}
}
TestMain.java
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class TestMain {
public static void main(String[] args) {
String xmlPath = "C:\\" + File.separator + "customer.xml";
try {
File file = new File(xmlPath);
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] {Company.class,Address.class,Costumer.class});
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Company customer = (Company) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Outout:
Company [custList=[Customer [name=Ram, age=25, id=0, addressList=[Address [city=Bangalore, country=India], Address [city=Patna, country=India]]], Customer [name=Ashu, age=26, id=0, addressList=[Address [city=Delhi, country=India], Address [city=Madhubani, country=India]]]]]
This is because the sub-elements of that class you are creating JAXBcontext instance ,doesn't have the same name as of the element names defined inside it.
Example:
#XmlType(name = "xyz", propOrder = { "a", "b", "c", "d" })
#XmlRootElement(name = "testClass")
public class TestClass
{
#XmlElement(required = true)
protected Status status;
#XmlElement(required = true)
protected String mno;
#XmlElement(required = true)
}
In the above class you don't have "xyz" , but if you will put the property name that is not available JAXBContext instantiation throws IlligalAnnotationException.
If anyone is curious about the usage of JAXB and Lombok.
My fix was to remove the getter and setter from the root object.