JAXB collection mapping - java

I'm new in dealing with XML in Java, but one of services I use returns it as result. Until now, I've dealt with mapping XML into POJO, using #XmlRootElement-like annotations. But now I have absolutely no idea to do with this document:
<?xml version="1.0" encoding="windows-1251"?>
<response>
<status>
<code>0</code>
</status>
<result>
<limit>2</limit>
...
<data>
<row0>
<ID>85427</ID>
<name>Default</name>
<siteID>40628</siteID>
... some elements
</row0>
</data>
</result>
</response>
Until now, I used these classes to bind XML (except 'data' node) into POJO:
#XmlRootElement(name="response")
public class Response {
private Status status;
private String result;
public Status getStatus() {
return status;
}
#XmlElement(name = "status")
public void setStatus(Status status) {
this.status = status;
}
public String getResult() {
return result;
}
#XmlElement(name ="result")
public void setResult(String result) {
this.result = result;
}
}
#XmlRootElement(name = "status")
public class Status {
private String ID;
private String code;
private String error;
public String getID() {
return ID;
}
#XmlElement(name = "ID")
public void setID(String ID) {
this.ID = ID;
}
public String getCode() {
return code;
}
#XmlElement(name = "code")
public void setCode(String code) {
this.code = code;
}
public String getError() {
return error;
}
#XmlElement(name = "error")
public void setError(String error) {
this.error = error;
}
}
But now I need to bind content as collection of elements. I've looked for examples, and everywhere people use specific tag to define root element for collection's item, but in this document, root tags will be as <row0>, <row1> etc.
I use Jackson, which, if I understand correctly, uses JAXB annotations to define XML to POJO bind rules. So could this deal be solved this way, or I have to manipulate this document in DOM-style?

You can solve your problem by using something like this :
Create your Row class that represents your <row0>,<row1> etc... and map it with JAXB like you would do it normally.
Then create a class that extends XmlAdapter<List<Row>,List<Element>> and define the abstracts methods marshall and unmarshall.
Here is some Javadoc to help you :
XmlAdapter : http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/XmlAdapter.html
Element : http://docs.oracle.com/javase/1.5.0/docs/api/org/w3c/dom/Element.html
Then create a Data class :
public class Data{
private List<Row> rows;
public List<Row> getRows() {
return rows;
}
#XmlAnyElement
#XmlJavaTypeAdapter(MyRowsAdapter.class)
public void setRows(List<Row> result) {
this.rows = rows;
}
}
Then you can add this mapping to your Response class :
private Data data;
public Data getData() {
return data;
}
#XmlElement(name="data")
public void setData(Data data) {
this.data = data;
}
Note that for this solution to work, your <data> element must only contains elements like your Row.
Also, you cannot use #XmlElementWrapper instead of using a Data class because of a bug in JAXB which make incompatible #XmlElementWrapper and #XmlJavaTypeAdapter : https://java.net/jira/browse/JAXB-787

Related

JaxB: How Do I Retrieve Text Attribute from Nested Element?

I want the Country class to store the "ISO_3166-1_Alpha-2_Code" code. The code currently gets back the "ISO_3166-1_Numeric-3_Code" code. Can't figure out how to tweak the Country class to get the specific attribute I want.
XML:
<?xml version='1.0' encoding='UTF-8'?>
<wd:Message_Event_Configuration">
<wd:Message_Event_Configuration_Data>
<wd:Country_Reference wd:Descriptor="Saint Martin">
<wd:ID wd:type="WID">66b7082a21e510000961bb6d82b5002a</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">MF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">MAF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">663</wd:ID>
</wd:Country_Reference>
<wd:Country_Reference wd:Descriptor="Saint Barthelemy">
<wd:ID wd:type="WID">881527f6cec910000ba81e8dccf61127</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">BL</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">BLM</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">652</wd:ID>
</wd:Country_Reference>
</wd:Message_Event_Configuration_Data>
</wd:Message_Event_Configuration>
Country List:
#XmlRootElement(name = "Message_Event_Configuration")
#XmlAccessorType(XmlAccessType.FIELD)
public class Countries {
#XmlElementWrapper(name = "Message_Event_Configuration_Data")
#XmlElement(name = "Country_Reference")
private List<Country> countries = new ArrayList<Country>();
public List<Country> getCountries() {
return countries;
}
public void setCountries(List<Country> countries) {
this.countries = countries;
}
}
Country:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Country_Reference")
public class Country {
#XmlElement(name = "ID")
private String isoCode;
public Country() {
}
public Country(String isoCode) {
this.isoCode = isoCode;
}
#XmlAttribute(name = "ISO_3166-1_Alpha-2_Code")
public String getISOCode() {
return isoCode;
}
public void setISOCode(String isoCode) {
this.isoCode = isoCode;
}
}
The <Country_Reference> XML element contains the ISO codes in a rather
sophisticated way within several <wd:ID> XML elements.
It is therefore much too simple to model them as a Java String property.
Instead, you need to model the Java-structure with more similarity to the XML-structure.
The sequence of XML elements <wd:ID> can be modeled by a property List<ID> idList
which needs to be annotated by#XmlElement(name="ID") .
The XML attribute wd:Descriptor="...." can be modeled by a property String descriptor
which needs to be annotated by #XmlAttribute(name="Descriptor").
For your convenience you can add an all-arguments-constructor and some methods for getting
the WID and ISO codes from the List<ID>.
#XmlAccessorType(XmlAccessType.FIELD)
public class Country {
#XmlAttribute(name = "Descriptor")
private String descriptor;
#XmlElement(name = "ID")
private List<ID> idList;
public Country() {
}
public Country(String descriptor, String wid, String isoAlpha2Code, String isoAlpha3Code, String isoNumeric3Code) {
this.descriptor = descriptor;
idList = new ArrayList<>();
idList.add(new ID("WID", wid));
idList.add(new ID("ISO_3166-1_Alpha-2_Code", isoAlpha2Code));
idList.add(new ID("ISO_3166-1_Alpha-3_Code", isoAlpha3Code));
idList.add(new ID("ISO_3166-1_Numeric-3_Code", isoNumeric3Code));
}
public String getWid() {
return getIdByType("WID");
}
public String getIsoAlpha2Code() {
return getIdByType("ISO_3166-1_Alpha-2_Code");
}
public String getIsoAlpha3Code() {
return getIdByType("ISO_3166-1_Alpha-3_Code");
}
public String getIsoNumeric3Code() {
return getIdByType("ISO_3166-1_Numeric-3_Code");
}
private String getIdByType(String idType) {
for (ID id : idList) {
if (id.getType().equals(idType))
return id.getValue();
}
return null;
}
}
The XML elements <wd:ID> are quite complex. Therefore we need a separate POJO class for modeling them.
Let's call the class ID.
The XML text between <wd:ID ..> and </wd:ID> is modeled by the property String value
which needs to be annotated by #XmlValue.
The XML attribute wd:type="..." is modeled by the property String type
which needs to be annotated by #XmlAttribute.
For convenient use by the class Country above, an all-arguments-constructor is added.
#XmlAccessorType(XmlAccessType.FIELD)
public class ID {
#XmlAttribute
private String type;
#XmlValue
private String value;
public ID() {
}
public ID(String type, String value) {
this.type = type;
this.value = value;
}
// public getters and setters (omitted here fro brevity)
}
The screenshot below (taken from within the debugger) visualizes the Java structure
and confirms that the unmarshalling of your XML example works correctly:

jaxb delete list tag

I need to marshall a java class to get a xml, but i don't know how to delete a tag inside the one generated.
I have a class with an object list with this form
#XmlRootElement(name = "Element")
public class Element {
private List<Foo> foos;
#XmlElementWrapper("fooList")
public List<Foo> getfoos() {
return foos;
}
public void setFoos(List<Foo> foos) {
this.foos = foos;
}
}
And the class Foo of the list is lie this:
#XmlRootElement
public class Foo {
private String id;
private String code;
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
When marshalling to get xml I get this:
<Element>
<fooList>
<foos>
<string1>asd</string1>
<string2>qwe</string2>
</foos>
<foos>
<string1>poi</string1>
<string2>lkj</string2>
</foos>
</fooList>
</Element>
But I want to get it without the tag foos, like this:
<Element>
<fooList>
<string1>asd</string1>
<string2>qwe</string2>
<string1>poi</string1>
<string2>lkj</string2>
</fooList>
</Element>
Can anyone help me?
Thanks a lot!!
You could do something like this:
#XmlRootElement(name = "Element")
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlElementWrapper(name = "fooList")
#XmlElements({
#XmlElement(name = "id", type = Id.class),
#XmlElement(name = "code", type = Code.class),
})
private List<FooItem> foos;
public List<FooItem> getfoos() {
return foos;
}
public void setFoos(List<FooItem> foos) {
this.foos = foos;
}
}
and then Id and Code classes look similar:
public class Id implements FooItem {
#XmlValue
private String id;
public Id() {}
public Id(String id) {
this.id = id;
}
}
They are bounded by an interface that doesn't do much:
public interface FooItem { }
This structure will allow you to marshal into xml as the one you specified you need.
The challenge with the class structure you had is that class Foo had 2 fields and #XmlValue can be applied only to one field per class. So having 2 fields "forces" them to stand for #XmlElement and they in turn have to be children of an xml element. This is why you had the "intermediate" foo elements in your xml for each Foo instance in your List.

Marshalling Java object to XML with JAXB returns unexpected output

I am having issues marshalling a bean into XML using JAXB. I have multiple REST API endpoints and I want to return a uniform response from all the endpoints, like the following:
<response>
<responseHeader> <!-- this will be same for all the end points -->
<status>OK</status>
<stausCode>AB-123<statusCode>
</responseHeader>
<responseBody>
<!-- contains end point specific data, could be differnet-->
</responseBody>
</response>
So what I did is created a generic response DTO:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "response")
public class GenericResponseDto implements Serializable {
#XmlElement(name="responseHeader")
private GenericResponseHeaderDto responseHeader;
#XmlAnyElement(name="responseBody")
private Object responseBody;
public GenericResponseHeaderDto getResponseHeader() {
return responseHeader;
}
public void setResponseHeader(GenericResponseHeaderDto responseHeader) {
this.responseHeader = responseHeader;
}
public Object getResponseBody() {
return responseBody;
}
public void setResponseBody(Object responseBody) {
this.responseBody = responseBody;
}
}
Where the response body field will be replaced by the following object for one of the endpoint responses:
#XmlRootElement(name = "responseBody")
#XmlAccessorType(XmlAccessType.FIELD)
public class Person implements Serializable {
#XmlElement(required = false)
private String phoneNumber;
#XmlElement(required = false)
private Integer personId;
public Integer getPersonId() {
return personId;
}
public void setPersonId(Integer personId) {
this.personId = personId;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
#Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
Here is my Jersey endpoint:
#POST
#Path("/myAPIFirstEndPoint")
#Produces({MediaType.APPLICATION_XML})
public GenericResponseDto myAPIFirstEndPoint(ABC abc) {
// some work and getting person dto
Person person = someWork.doWork();
GenericResponseDto genericResponseDto = new GenericResponseDto();
// not setting any responseHeader for now, so ignore
genericResponseDto.setResponseBody(row);
return genericResponseDto;
}
But it's not working as expected. The toString() method is being called on the Person object, instead of it being marshalled to XML. I'm getting the following incorrect response:
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<responseBody>
path.to.package.Person#36af3690[phoneNumber=+123456789,personId=-1]
</responseBody>
</response>
Can you please tell me what I'm doing wrong? I am using Jersey and JAXB with Spring.
EDIT:
Introduced generics:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "response")
#XmlSeeAlso({Person.class})
public class GenericResponseDto<T> implements Serializable {
#XmlElement(name="responseHeader")
private GenericResponseHeaderDto responseHeader;
#XmlElement(name="responseBody")
private T responseBody;
public GenericResponseHeaderDto getResponseHeader() {
return responseHeader;
}
public void setResponseHeader(GenericResponseHeaderDto responseHeader) {
this.responseHeader = responseHeader;
}
public T getResponseBody() {
return responseBody;
}
public void setResponseBody(T responseBody) {
this.responseBody = responseBody;
}
}
changed Jersey endpoint as follows:
#POST
#Path("/myAPIFirstEndPoint")
#Produces({MediaType.APPLICATION_XML})
public GenericResponseDto<Person> myAPIFirstEndPoint(ABC abc) {
// some work and getting person dto
Person person = someWork.doWork();
GenericResponseDto<Person> genericResponseDto = new GenericResponseDto<Person>();
// not setting any responseHeader for now, so ignore
genericResponseDto.setResponseBody(row);
return genericResponseDto;
}
Still getting the same response as mentioned above.
Now getting this response, after adding #XmlSeeAlso({Person.class}) in GenericResponseDto
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<responseBody xsi:type="Person">
<phoneNumber>+923454502dd0559</phoneNumber>
<personId>-1</personId>
<token />
</responseBody>
</response>

JAXB EclipseLink Moxy add template xml

i have problem with my jaxb EclipseLink implementation.
Let's assume I have the following Entity ...
#XmlRootElement(name = GenericConfigEntity.XML_ROOT_TAG)
public class GenericConfigEntity {
private String name;
private String data;
private String version;
private String date;
private String template;
#XmlAttribute(name = GenericConfigEntity.XML_NAME)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(name = GenericConfigEntity.XML_DATA)
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
#XmlAttribute(name = GenericConfigEntity.XML_VERSION)
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
#XmlAttribute(name = GenericConfigEntity.XML_DATE)
public String getDate() {
return date;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
}
The string 'template' contains xml data already let's say someting like this (in my real context it is a lot more and I do not want to create the entities for this).
<Prozess name="xx" test="1">
<Debug system="test" />
</Prozess>
Now my question is if there is a way to integrate the template string into the marshalling process that someting like this is generated
<conf name="xx" version="x" datum="xx">
<Prozess name="xx" test="1">
<Debug system="test" />
</Prozess>
<Data>
TextTextText
</Data>
</conf>
It is no solution to wrap the template in an tag because i am restricted to this layout.
Also #XmlValue is no solution because I get an exception "all other elements have to be an attribute because one is marked as xmlvalue".
I haven't used this myself yet, but I reckon you could use the #XmlAnyElement in conjunction with a DomHandler to implement such a mapping. You can find a helpful example in the blog of MOXy's lead programmer here.
I think you'd still have to make sure that the XML content in your template field has at least an opening and a closing tag, which serve to identify the DOM element during (un)marshalling. That tag may be arbitrary as required by you. I guess you could just look for the first tag appearing in the string and try to match it against the end of the string.

How do I read attributes using jaxb?

Given this XML:
<response>
<detail Id="123" Length="10" Width="20" Height="30" />
</response>
This is what I have now, but it is not working (I'm getting empty result):
#XmlRootElement(name="response")
public class MyResponse {
List<ResponseDetail> response;
//+getters +setters +constructor
}
public class MyResponseDetail {
Integer Id;
Integer Length;
Integer Width;
Integer Height;
//+getters +setters
}
I'm making a call to a remote service using RestOperations and I want to parse the <detail ..> element. I've tried passing both MyResponse and MyResponseDetail classes to RestOperations but the result is always empty.
What should my object structure look like to match that XML?
You need to annotate your classes like that:
#XmlRootElement
public class Response {
private List<Detail> detail;
public void setDetail(List<Detail> detail) {
this.detail = detail;
}
public List<Detail> getDetail() {
return detail;
}
}
public class Detail {
private String id;
/* add other attributes here */
#XmlAttribute(name = "Id")
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}

Categories

Resources