1

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>
ihaider
  • 1,290
  • 4
  • 19
  • 38
  • You should try introducing generics, and return `GenericResponseDto` as the return type – Paul Samsotha May 22 '16 at 15:46
  • @peeskillet have edited the answer as per your suggested change. Still facing same issues. Can you guide me for an alternate? – ihaider May 22 '16 at 16:36
  • The only other thing I can think is try adding `@XmlSeeAlso({Person.class})` on the `GenericResponseDto` class. – Paul Samsotha May 22 '16 at 16:41
  • update again. Thanks it worked. So for each API end point. Say the response object is different. I will have to add that class to `GenericResponseDto` inside `XmlSeeAlso` ? is that the correct approach ? – ihaider May 22 '16 at 17:04
  • @peeskillet update question with your suggested changes. Please see above comment ^ Thanks. Appreciate your help. – ihaider May 22 '16 at 19:19
  • Seems that may be the case :/ I though you could just use generics, but I guess not – Paul Samsotha May 23 '16 at 00:32
  • @peeskillet so you think this isn't an optimal solution ? Any alternative you could suggest? Thanks. – ihaider May 23 '16 at 08:39
  • I personally don't like it. You can try to use [Jackson for xml](http://stackoverflow.com/a/36480647/2587435) instead of JAXB – Paul Samsotha May 23 '16 at 08:41
  • @peeskillet did some research on integrating jackson xml with jersey and I must say I didn't find any thing that will be helpful for my scenario (i.e. Generic Response) I've already integrated `@XmlSeeAlso({Person.class})` fix for now and it's working fine. What do you think should I go ahead with it or you can pin point some issues that I could get in future using this fix. – ihaider May 25 '16 at 17:00

0 Answers0