Skip to content

Support @XmlSeeAlso in Jaxb2XmlDecoder #30167

Closed
@elkhart

Description

@elkhart

While introducing WebClient into my current project I ran into the issue that the call

response.bodyToMono(RealEstate::class.java)

causes the error

org.springframework.core.codec.DecodingException: Could not unmarshal XML to class test.RealEstate; nested exception is javax.xml.bind.UnmarshalException
 - with linked exception:
[com.sun.istack.SAXParseException2; lineNumber: 2; columnNumber: 1; Unable to create an instance of test.RealEstate]

	at org.springframework.http.codec.xml.Jaxb2XmlDecoder.unmarshal(Jaxb2XmlDecoder.java:242)
	Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: 

We have the following simplified class hierarchy:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "RealEstate", namespace = "http://anything.you.want", propOrder = {"address"})
@XmlSeeAlso({House.class, Apartment.class})
public abstract class RealEstate {
    protected String address;

    public String getAddress() { return address; }

    public void setAddress(String address) { this.address = address; }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Apartment", propOrder = {"livingArea"}, namespace = "http://anything.you.want")
@XmlRootElement(name = "apartment", namespace = "http://anything.you.want")
public class Apartment extends RealEstate {
    private String livingArea;

    public String getLivingArea() {
        return livingArea;
    }

    public void setLivingArea(String livingArea) {
        this.livingArea = livingArea;
    }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "House", propOrder = {"plotArea"}, namespace = "http://anything.you.want")
@XmlRootElement(name = "house", namespace = "http://anything.you.want")
public class House extends RealEstate {
    private String plotArea;

    public String getPlotArea() {
        return plotArea;
    }

    public void setPlotArea(String plotArea) {
        this.plotArea = plotArea;
    }
}

One possible response body to be decoded is

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<objects:house xmlns:objects="http://anything.you.want">
  <address>Somewhere around the corner</address>
  <plotArea>small</plotArea>
</objects:house>

Assumed that there is nothing really wrong with the given simplified example
the unmarshal seems to be the issue.

Unmarshaller unmarshaller = initUnmarshaller(outputClass);
XMLEventReader eventReader = StaxUtils.createXMLEventReader(events);
if (outputClass.isAnnotationPresent(XmlRootElement.class)) {
  return unmarshaller.unmarshal(eventReader);
}
else {
  JAXBElement<?> jaxbElement = unmarshaller.unmarshal(eventReader, outputClass);
  return jaxbElement.getValue();
}

Since RealEstate is not annotated as XmlRootElement the else block is executed which causes the error.
Using the other method without handing over the target class would work.

I cannot say if the Unmarshaller#unmarshal(XMLEventReader reader, Class<T> declaredType ) method is supposed to work when using XMLSeeAlso as described in the simplified example.

Looking into how the RestTemplate solved the task it boils down to the MarshallingHttpMessageConverter doing

Object result = this.unmarshaller.unmarshal(source);
if (!clazz.isInstance(result)) {
  throw new TypeMismatchException(result, clazz);
}
return result;

It seems that the Jaxb2XmlDecoder differentiates for some reason while the MarshallingHttpMessageConverter is doing the type check after unmarshalling.

Also asked at stackoverflow without getting any response.

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions