Search code examples
javaxmlhttprequestjaxbxml-deserializationfeign

Why is jaxb not unmarshalling this XML document into a Java object?


Thank you for taking the time to read.

Before asking I would like to point out that I've read as many similar posts on StackOverflow / the internet that I possibly can.

My goal is to deserialize the response from an API request into a usable java object.

I am sending an POST request to an endpoint to create a job in our schedule. The job is created successfully and the following XML is returned in the body:

<entry xmlns="http://purl.org/atom/ns#">
    <id>0</id>
    <title>Job has been created.</title>
    <source>com.tidalsoft.framework.rpc.Result</source>
    <tes:result xmlns:tes="http://www.auto-schedule.com/client">
        <tes:message>Job has been created.</tes:message>
        <tes:objectid>42320</tes:objectid>
        <tes:id>0</tes:id>
        <tes:operation>CREATE</tes:operation>
        <tes:ok>true</tes:ok>
        <tes:objectname>Job</tes:objectname>
    </tes:result>
</entry>

However, when I try to unmarshal this into a POJO, the mapping is not working as expected.

For the sake of simplicity, I am trying to capture only the first fields, id, title, and source (I have tried to capture only a single field -- id -- and I have also tried to do all fields to no avail).

Here's what the POJO looks like:

@XmlRootElement(name = "entry", namespace = "http://purl.org/atom/ns#")
@XmlAccessorType(XmlAccessType.FIELD)
public class Response {

    @XmlElement(name = "id")
    private String id;

    @XmlElement(name = "title")
    private String title;

    @XmlElement(name = "source")
    private String source;

    public Response() {}
}

In order to check whether or not the Xml elements are captured, I'm logging the attributes, which are null:

Response{id='null', title='null', source='null'}

Feign is the HTTP client sending out the requests, and here is the client file:

@FeignClient(name="ReportSchedulerClient", url = "https://scheduler.com", configuration = FeignClientConfiguration.class)
public interface ReportSchedulerClient {

    @PostMapping(value = "/webservice", consumes = "application/xml", produces = "text/xml")
    Response sendJobConfigRequest(@RequestBody Request request);

}

and a simple custom config file for auth:

public class FeignClientConfiguration {
    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "pass");
    }
}

I am trying to avoid explicitly unmarshalling the file, but I have also tried to unmarshall the request explicitly using something like this:

Response response = (Response) unmarshaller.unmarshal(new StreamSource(new StringReader(response.body().toString())));

Please let me know if you have any advice, if anything is wrong with my code, or any alternative suggestions. Thanks in advance.


Solution

  • You need to specify the namespace at the element level. For example:

    @XmlElement(name = "id", namespace = "http://purl.org/atom/ns#")
    private String id;
    

    To set a default namespace you can do it at the package level, creating the package-info.java file in the package folder with a content like this:

    @XmlSchema(
        namespace = "http://purl.org/atom/ns#",
        elementFormDefault = XmlNsForm.QUALIFIED)
    package your.model.package;
    
    import javax.xml.bind.annotation.XmlNsForm;
    import javax.xml.bind.annotation.XmlSchema;
    

    Besides, as you are explicitly adding @XmlElement to all your fields, you can remove the @XmlAccessorType(XmlAccessType.FIELD) annotation, as it's purpose it's to map by default all fields to elements.