javax.xml.bind.MarshalException - 带有链接异常:[javax.xml.bind.JAXBException: class ** 或其任何超类在此上下文中都是已知的

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20686351/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:35:59  来源:igfitidea点击:

javax.xml.bind.MarshalException - with linked exception: [javax.xml.bind.JAXBException: class ** nor any of its super class is known to this context

javaweb-servicesjaxb

提问by Apps

I have a generic web service which is expecting a WebServiceRequestobject. This object has a payload which is of type Object. Below is the type of my payload.

我有一个通用的 Web 服务,它需要一个WebServiceRequest对象。此对象具有类型为 Object 的有效负载。下面是我的有效载荷的类型。

<xs:complexType name="payload">
    <xs:sequence>
        <xs:any processContents="lax"></xs:any>
    </xs:sequence>
</xs:complexType>

I created JAXBclasses for the web service input and output types. So for payload, this the field that was generated.

JAXB为 Web 服务输入和输出类型创建了类。所以对于有效载荷,这是生成的字段。

 @XmlAnyElement(lax = true)
 private Object any;

Below is the structure of my JAXBgenerated WebServiceRequestVO.

下面是我JAXB生成的WebServiceRequestVO的结构。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "serviceRequest", namespace = "http://ws.test.svc.com/", propOrder = {
    "payload"
})
public class WebServiceRequest{
        @XmlElement
        private Payload payload;
} 

public class Payload{
 @XmlAnyElement(lax = true)
 private Object any;
}

I have some custom POJOs which I need to populate and set as the payload. I annotated these POJOs using the following annotation

我有一些自定义 POJO,我需要将它们填充并设置为有效负载。我使用以下注释对这些 POJO 进行了注释

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class AddressVO {
    @XmlElement
    private String pinCode;
    @XmlElement
    private String city;
}

I populated the data for this POJO and tried to set as the payload of WebServiceRequest. But when I do that, I'm getting the below exception.

我填充了这个 POJO 的数据并尝试设置为WebServiceRequest. 但是当我这样做时,我得到了以下异常。

javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.JAXBException: class com.vo.test.AddressVO nor any of its super class is known to this context.

Could you please suggest some ways to overcome this? In one linkit was mentioned to include @XmlSeeAlso, but i can't do that since my payload is very generic. Kindly help me in this regard.

你能提出一些克服这个问题的方法吗?在一个链接中提到 include @XmlSeeAlso,但我不能这样做,因为我的有效载荷非常通用。请在这方面帮助我。

采纳答案by My-Name-Is

In case that you can't apply the @XMLSeeAlsoannotation, you will need to create a custom MessageBodyReaderand MessageBodyWriterwhich are responsible to marshall and unmarshall between java and XML. Below there is shown an abstract implementation of a generic MessageBodyReaderwhich was actually intended to perfom a type specific XML validation. The writer is quite similar, hence it isn't added.

如果您不能应用@XMLSeeAlso注释,您将需要创建一个自定义MessageBodyReaderMessageBodyWriter负责在 java 和 XML 之间编组和解组。下面显示了一个泛型的抽象实现,MessageBodyReader它实际上是为了执行特定于类型的 XML 验证。作者很相似,所以不加了。

public abstract class AbstractXmlValidationReader<T> implements
        MessageBodyReader<T> {

    private final Providers providers;
    private final Schema schema;

    public AbstractXmlValidationReader(final Providers providers,
            final ServletContext servletContext, final String xsdFileName) {
        this.providers = providers;

        try {
            SchemaFactory sf = SchemaFactory
                    .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            File xsd = new File(servletContext.getRealPath(xsdFileName));
            schema = sf.newSchema(xsd);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to create XSD validation schema", e);
        }
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {

        @SuppressWarnings("unchecked")
        Class<T> readableClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];

        if (type == readableClass
                && type.isAnnotationPresent(XmlRootElement.class)) {
            return true;
        }
        return false;
    }

    @Override
    public T readFrom(Class<T> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
            throws IOException, WebApplicationException {

        try {
            JAXBContext jaxbContext = null;
            ContextResolver<JAXBContext> resolver = providers
                    .getContextResolver(JAXBContext.class, mediaType);
            if (null != resolver) {
                jaxbContext = resolver.getContext(type);
            }
            if (null == jaxbContext) {
                jaxbContext = JAXBContext.newInstance(type);
            }
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            unmarshaller.setSchema(schema);

            @SuppressWarnings("unchecked")
            T entity = (T) unmarshaller.unmarshal(entityStream);
            return entity;
        } catch (JAXBException e) {
            throw new MessageBodyReaderValidationException(
                    "Failure while performing xml validation or xml marhalling!",
                    e);
        }
    }
} 

And a concreate implementation for type Address

以及类型的 concreate 实现 Address

@Provider
@Consumes(MediaType.APPLICATION_XML)
public class AddressXmlValidationReader extends
        AbstractXmlValidationReader<Address> {

    private final static String xsdFileName = "/xsd/Address.xsd";

    public AddressXmlValidationReader(@Context Providers providers,
            @Context ServletContext servletContext) {
        super(providers, servletContext, xsdFileName);
    }
}

What you need now is a slighlty modified readFrommethod of the MessageBodyReaderwhich may look as followed. For the MessageBodyWriterthe method is called writeTo.

你现在需要的是一个稍微修改过的readFrom方法,MessageBodyReader它可能如下所示。因为该MessageBodyWriter方法被调用writeTo

@Override
public T readFrom(Class<T> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
        throws IOException, WebApplicationException {

    try {
        JAXBContext jaxbContext = null;
        ContextResolver<JAXBContext> resolver = providers
                .getContextResolver(JAXBContext.class, mediaType);

        if(entityStream != null){
            // TODO read the entityStream and determine the concrete type of the XML content
            type = ... ;
        }

        if (null != resolver) {
            jaxbContext = resolver.getContext(type);
        }
        if (null == jaxbContext) {
            jaxbContext = JAXBContext.newInstance(type);
        }
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        unmarshaller.setSchema(schema);

        @SuppressWarnings("unchecked")
        T entity = (T) unmarshaller.unmarshal(entityStream);
        return entity;
    } catch (JAXBException e) {
        throw new MessageBodyReaderValidationException(
                "Failure while performing xml validation or xml marhalling!",
                e);
    }
}

With this approach you define a general type via the concrete subclass of the Reader and Writer instance. And a possible more specific type can either be determine within the abstract base class (like in this example) or it's getted injected from somewhere else. Of course you can modify this MessageBodyReaderso that the concrete type fo the XML input is determined somewhere or somehow else. But in general this is the way how you will solve your issue.

使用这种方法,您可以通过 Reader 和 Writer 实例的具体子类定义通用类型。并且可能的更具体的类型可以在抽象基类中确定(如本例中),也可以从其他地方注入。当然,您可以修改它,MessageBodyReader以便在某处或以其他方式确定 XML 输入的具体类型。但总的来说,这就是您解决问题的方式。



Notice:

注意:

Don't forget to register the concrete Reader and writer implementation in the web services Applicationclass.

不要忘记在 Web 服务Application类中注册具体的 Reader 和 writer 实现。

@ApplicationPath("/services")
public class WSApplication extends Application {
    private Set<Object> singletons = new HashSet<Object>();
    private Set<Class<?>> classes = new HashSet<Class<?>>();
    public WSApplication() {
        ...
        classes.add(AddressXmlValidationReader.class);
        ...
    }
    ...
}