Yang opened SPR-10262 and commented

I have a Controller class returns a list wrapper because Jaxb won't work if just returning a List. So I built a wrapper class to use for the case where a list is returned.

@RequestMapping(produces= { "application/xml"} )
@ResponseBody
public JaxbListWrapper<Contact> listXML() {
    List<Contact> result = subnetService.findAllSubnets();
    return new JaxbListWrapper<>( result );
}

Here is the JaxbListWrapper:

@XmlRootElement(name="list")
public class JaxbListWrapper<T> {
    private List<T> items;

    public JaxbListWrapper() {
        items = new ArrayList<T>();
    }

    public JaxbListWrapper(List<T> items) {
        this.items = items;
    }

    @XmlAnyElement(lax=true)
    public List<T> getItems() {
        return items;
    }
}

(the above wrapper idea is from http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html)

But it wouldn't work in Spring MVC. And I will get this error of 'JAXBException: Contact nor any of its super class is known to this context'.

I looked into the code of Jaxb2RootElementHttpMessageConverter and realized that it is only creating a JaxbContext based on one class each. The JaxbContext doesn't contains Contact.class and thus the error.

One work around is to add @XmlSeeAlso({Contact.class}) in the JaxbListWrapper(). But that kind of defeated the purpose of a generic list wrapper.

I wonder: Option 1: if the Jaxb2RootElementHttpMessageConverter can be customized in a way so that can be passed in as the marshaller and unmarshaller. JaxbMarshaller is already thread safe. Option 2: Or better, looking for in the application context, if it is configured, just use that jaxbMarshaller as the default. It seems many people already assumed that if they config ', that would be what used by Jaxb2RootElementHttpMessageConverter .

This issue is similar to: http://forum.springsource.org/showthread.php?129342-Spring-Roo-and-JAXBException-X-nor-any-of-its-super-class-is-known-to-this-context


Affects: 3.2 GA, 3.2.1

Reference URL: http://forum.springsource.org/showthread.php?129342-Spring-Roo-and-JAXBException-X-nor-any-of-its-super-class-is-known-to-this-context

3 votes, 8 watchers

Comment From: spring-projects-issues

Rossen Stoyanchev commented

Have you considered using MarshallingHttpMessageConverter? It seems to match the behavior you want.

Comment From: spring-projects-issues

Yang commented

Thanks. Rossen. MarshallingHttpMessageConverter does work in this case. I tried the following and it solved the problem. I guess Jaxb2RootElementHttpMessageConverter is not designed for my use case. I wonder if Jaxb2RootElementHttpMessageConverter can be extended to cover more cases like mine. After all, it is the default converter and when it is not working, people spend a lot of time trying to figure it out.

    <oxm:jaxb2-marshaller id="marshaller">
        <oxm:class-to-be-bound
            name="xxxxx.JaxbListWrapper" />
        <oxm:class-to-be-bound
            name="xxxxx.Contact" />
    </oxm:jaxb2-marshaller>

    <mvc:annotation-driven conversion-service="applicationConversionService">
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                <property name="marshaller" ref="marshaller" />
                <property name="unmarshaller" ref="marshaller" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

Comment From: spring-projects-issues

Rossen Stoyanchev commented

Okay, good point. I'll take a look at making AbstractJaxb2HttpMessageConverter configurable with a Marshaller/Unmarshaller. In the very least the Javadoc for Jaxb2RootElementHttpMessageConverter can be updated to point to MarshallingHttpMessageConverter.

Comment From: spring-projects-issues

Dmitry Katsubo commented

I have hit similar problem when trying to customize JAXB marshaller. I my case I would like to tune marshaller before using it:

marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

However AbstractJaxb2HttpMessageConverter has protected *final* Marshaller createMarshaller(...) which makes it impossible to extend this class and add this behaviour. So the only way out is MarshallingHttpMessageConverter + spring-oxm module.

P.S. There is also inconsistency between Jaxb2RootElementHttpMessageConverter.canRead() that supports reading classes annotated with @XmlType and Jaxb2Marshaller.supportsInternal() that checks only for @XmlRootElement.

Comment From: spring-projects-issues

Eric Bottard commented

+1 for adding a hook to customize the JaxbContext (if not removing the final modifier entirely). This issue also pops up for example with Spring HATEOAS' PagedResources that end-user can't modify to add @XmlSeeAlso to her element class.

Comment From: spring-projects-issues

Dmitry Katsubo commented

Eric, if you like the approach I have suggested in #15096, vote for that issue. However it targets the MarshallingHttpMessageConverter+Jaxb2Marshaller couple rather than Jaxb2RootElementHttpMessageConverter. <oxm:jaxb2-marshaller> is alright for majority of use cases, but I personally miss JAXB property support via it.