我有一个通用的 Web 服务,它需要一个 WebServiceRequest
对象。该对象具有 Object 类型的有效负载。以下是我的有效载荷的类型。
<xs:complexType name="payload">
<xs:sequence>
<xs:any processContents="lax"></xs:any>
</xs:sequence>
</xs:complexType>
我为 Web 服务输入和输出类型创建了 JAXB
类。因此对于有效载荷,这是生成的字段。
@XmlAnyElement(lax = true)
private Object any;
下面是我的 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;
}
我有一些自定义 POJO,我需要将其填充并设置为负载。我使用以下注释对这些 POJO 进行了注释
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class AddressVO {
@XmlElement
private String pinCode;
@XmlElement
private String city;
}
我为这个 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.
您能否提出一些克服此问题的方法?一合link有人提到它包含 @XmlSeeAlso
,但我不能这样做,因为我的有效负载非常通用。请在这方面帮助我。
最佳答案
如果您不能应用 @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);
}
}
}
以及 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);
}
}
您现在需要的是对 MessageBodyReader
的 readFrom
方法进行轻微修改,如下所示。对于 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);
}
}
使用这种方法,您可以通过 Reader 和 Writer 实例的具体子类定义通用类型。一个可能的更具体的类型可以在抽象基类中确定(就像在这个例子中)或者它是从其他地方注入(inject)的。当然,您可以修改此 MessageBodyReader
,以便在某处或其他地方确定 XML 输入的具体类型。但总的来说,这就是您解决问题的方式。
注意:
不要忘记在 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);
...
}
...
}
关于javax.xml.bind.MarshalException - 链接异常 : [javax. xml.bind.JAXBException: class ** 其父类(super class)在此上下文中不为人所知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20686351/