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
javax.xml.bind.MarshalException - with linked exception: [javax.xml.bind.JAXBException: class ** nor any of its super class is known to this context
提问by Apps
I have a generic web service which is expecting a WebServiceRequest
object. 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 JAXB
classes 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 JAXB
generated WebServiceRequest
VO.
下面是我JAXB
生成的WebServiceRequest
VO的结构。
@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 @XMLSeeAlso
annotation, you will need to create a custom MessageBodyReader
and MessageBodyWriter
which are responsible to marshall and unmarshall between java and XML. Below there is shown an abstract implementation of a generic MessageBodyReader
which was actually intended to perfom a type specific XML validation. The writer is quite similar, hence it isn't added.
如果您不能应用@XMLSeeAlso
注释,您将需要创建一个自定义MessageBodyReader
并MessageBodyWriter
负责在 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 readFrom
method of the MessageBodyReader
which may look as followed. For the MessageBodyWriter
the 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 MessageBodyReader
so 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 Application
class.
不要忘记在 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);
...
}
...
}