Search code examples
javaspringdeserializationresttemplateobjectmapper

RestTemplate XML deserialize null on empty tag with attributes


I have the following response, when values are in attributes and my tag has no values except attributes (also level and my can have multiple values)

<main>
<level id="1">
<my id="111" amount="100000.00"/>
</level>
<level id="2">
<my id="121" amount="5000.00"/>
</level>
<level id="3">
<my id="122" amount="500.00"/>
</level>
</main>

When trying to deserialize my is null

  ResponseEntity<RequestVO> jackpotFeederResponse = restTemplate.exchange(uriBuilder.build(), HttpMethod.GET,
                    HttpEntity.EMPTY, RequestVO.class);

I try adding different DeserializationFeature, but failed

            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, true);
            objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
            converter.setObjectMapper(objectMapper);
            restTemplate.getMessageConverters().add(converter);

My POJOs:

@XmlRootElement(name = "main", namespace = "")
@Data
public class RequestVO implements Serializable  {

    private static final long serialVersionUID = -1382015668147149795L;

    @JacksonXmlProperty(localName = "level")
    ArrayList<LevelVO> levelList;
}


@XmlRootElement(name = "level", namespace = "")
@Data
public class LevelVO implements Serializable {

    private static final long serialVersionUID = -1382015668147149795L;

    @JacksonXmlProperty(localName = "id", isAttribute = true)
    String id;
    @JacksonXmlProperty(localName = "my")
    ArrayList<MyVO> myList;


@XmlRootElement(name = "my", namespace = "")
@Data
public class MyVO implements Serializable  {

    private static final long serialVersionUID = -1382015668147149795L;

    @JacksonXmlProperty(localName = "id", isAttribute = true)
    String id;

Solution

  • First of all you should use the @JacksonXmlElementWrapper(useWrapping = false) on your collection types. Next up try not to mix JAXB Annotations with Jackson ones. Therefor just use @JacksonXmlRootElement instead of @XmlRootElement.

    Your XML POJOs should look like this:

    @JacksonXmlRootElement(localName = "main")
    @Data
    public class RequestVO implements Serializable {
        @JacksonXmlElementWrapper(useWrapping = false)
        @JacksonXmlProperty(localName = "level")
        ArrayList<LevelVO> levelList;
    }
    
    
    @JacksonXmlRootElement(localName = "level")
    @Data
    public class LevelVO implements Serializable {
        @JacksonXmlProperty(localName = "id", isAttribute = true)
        String id;
    
        @JacksonXmlElementWrapper(useWrapping = false)
        @JacksonXmlProperty(localName = "my")
        ArrayList<MyVO> myList;
    }
    
    @JacksonXmlRootElement(localName = "my")
    @Data
    public class MyVO implements Serializable {
        @JacksonXmlProperty(localName = "id", isAttribute = true)
        String id;
    
        @JacksonXmlProperty(localName = "amount", isAttribute = true)
        String amount;
    }
    

    If you really want to stick to use the @XmlRootElement Annotation. It could be that you also have to register the JaxbAnnotationModule to Jacksons XmlMapper:

    JaxbAnnotationModule module = new JaxbAnnotationModule();
    xmlMapper.registerModule(module);