How do I read attributes using jaxb? - java

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;
}
}

Related

How to use #XmlIDREF and #XmlID

I don't quite understand how #XmlIDREF and #XmlID work together. By using XmlIDREF I only create a reference to the actual element. However what is the use case for XmlID.
I want to create a reference to the class Publication. Is it enough to annotate the publication List with #XmlIDREF?
public class Author {
private String id;
private String name;
private List<Publication> publications = new LinkedList<>();
public Author() {
super();
}
#XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlIDREF
public List<Publication> getPublications() {
return publications;
}
I want to create a reference to the class Publication.
Is it enough to annotate the publication List with #XmlIDREF?
No, that's only one half of what you need.
You already have this:
With #XmlIDREF you mark the referencing side of the relation
(pointing from Author to Publication).
public class Author {
...
#XmlIDREF
#XmlElement(name = "publication")
public List<Publication> getPublications() {
return publications;
}
...
}
You also need to mark the referenced side (the Publication itself)
by annotating one of its properties with #XmlID, for example like this:
public class Publication {
...
#XmlID
#XmlElement
public String getId() {
return id;
}
...
}
Then you are able to process XML content like this example:
<root>
<publication>
<id>p-101</id>
<title>Death on the Nile</title>
</publication>
<publication>
<id>p-102</id>
<title>The murder of Roger Ackroyd</title>
</publication>
...
<author>
<id>a-42</id>
<name>Agatha Christie</name>
<publication>p-101</publication>
<publication>p-102</publication>
</author>
...
</root>
You see, the XML references (like <publication>p-101</publication>)
are mapped to Java object references (in List<Publication> publications).

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.

JAXB collection mapping

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

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>

Spring RestTemplate + map XML result to Domain object

With the use of Spring RestTemplate, how can map following XML result to some Domain Object?
As a solution I have designed following Domain classes but I am wonder whether how can I map those contain values (ex: 100, 200, 300) to domain object property.
Thanks.
XML result
<counting>
<value id="1" name="Robin" date="2015-09-03">100</value>
<value id="2" name="Joy" date="2015-09-03">200</value>
<value id="3" name="Tan" date="2015-09-03">300</value>
<counting>
Domain Class
#XmlRootElement(name = "counting")
public class Count {
private Value value;
public Count() {}
// Getters and setters
}
#XmlRootElement(name = "value")
public class Value {
public Value() {}
private long id;
private String name;
private Date date;
// Getters and setters
}
I have solved the issue with following data model
#XmlRootElement(name = "counting")
public class Count {
private List<Value> value;
public Count() {}
// Getters and setters
#XmlElement
public List<Value> getValue() {
return value;
}
public void setValue(List<Value> value) {
return this.value = value;
}
}
#XmlAccessorType(XmlAccessorType.FIELD)
public class Value {
public Value() {}
#XmlAttribute
private long id;
#XmlAttribute
private String name;
#XmlAttribute
private String date;
#XmlValue
private String xmlValue;
// Getters and setters
}

Categories

Resources