java - 如何使用 MessageBodyReader 读取对象集合

标签 java rest jaxb xsd jax-rs

我有某种类型的 XSD 架构,我们将其命名为 A。

然后我尝试使用 XSD 架构验证编写 MessageBodyReader - 如果我在那里传递单个对象,它就可以工作。但我怎样才能让它读取这些类型的集合呢?

我稍后使用该读取器作为 REST 服务的输入参数,并将其注册到 javax.ws.rs.core.Application 中。

@Provider
@Produces(MediaType.APPLICATION_XML)
public class AReader implements MessageBodyReader < A >
{
private static final String XSD = "/a.xsd";

@Override
public boolean isReadable(Class < ? > type, Type genericType, Annotation[] arg2, MediaType mediaType)
{
    return MediaType.APPLICATION_XML_TYPE.equals(mediaType) && A.class.isAssignableFrom(type);
}

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

    try
    {
        JAXBContext jaxbContext = JAXBContext.newInstance(A.class);
        try
        {
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

            unmarshaller.setSchema(getSchema());

            try
            {
                return (A) unmarshaller.unmarshal(entityStream);
            }
            catch (JAXBException e)
            {
                throw new RuntimeException(e);
            }
        }
        catch (SAXException e)
        {
            throw new RuntimeException(e);
        }

    }
    catch (JAXBException e)
    {
        throw new RuntimeException(e);
    }

}

private Schema getSchema() throws SAXException
{
    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

    Schema schema = schemaFactory.newSchema(A.class.getResource(XSD));
    return schema;
}

最佳答案

所以事情是这样的。 XML 文档必须有一个根元素,因此您不能只发送类似

<domain></domain>
<domain></domain>

您需要将其包装在另一个根元素中,例如

<domains>
    <domain></domain>
    <domain></domain>
</domains>

话虽这么说,xsd 架构还需要反射(reflect)其他根元素。你不能只拥有 <domain>已定义元素并期望根据 <domains> 进行验证.

一旦你把它放在你的xsd中,就像这样

<xsd:element name="domains">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element ref="domain" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

<xsd:element name="domain">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element name="id" type="xsd:int"/>
            <xsd:element name="name" type="xsd:string"/>
        </xsd:sequence> 
    </xsd:complexType>
</xsd:element>

然后我们就可以制作 Domains包装类,以及 Domain

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Domain {

    @XmlElement
    private int id;
    @XmlElement
    private String name;
    // Getters and Setters
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Domains {

    @XmlElementRef(name = "domain")
    private List<Domain> domains;
    // Getter and Setters
}

然后我们可以创建MessageBodyReader接受 DomainsDomain类型。类似的东西

@Provider
@Consumes(MediaType.APPLICATION_XML)
public class DomainMessageBodyReader implements MessageBodyReader {
    private static final String XSD_PATH = "path/to/domains.xsd";

    @Context
    private Providers providers;
    private Schema schema;

    public DomainMessageBodyReader()  {
        try {
            initSchema();
        } catch (Exception ex) {
            Logger.getLogger(DomainMessageBodyReader.class.getName())
                                         .log(Level.SEVERE, null, ex);
            throw new InternalServerErrorException();
        }
    }

    private void initSchema() throws Exception  {
        SchemaFactory factory 
                = SchemaFactory.newInstance(
                                     XMLConstants.W3C_XML_SCHEMA_NS_URI);
        schema = factory.newSchema(new File(XSD_PATH));
    }

    @Override
    public boolean isReadable(Class type, Type type1, 
            Annotation[] antns, MediaType mt) {
        return type == Domain.class || type == Domains.class;
    }

    @Override
    public Object readFrom(Class type, Type type1, Annotation[] antns, 
            MediaType mt, MultivaluedMap mm, InputStream in) 
            throws IOException, WebApplicationException {
        try {

            JAXBContext context = JAXBContext.newInstance(Domains.class, 
                                                          Domain.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            unmarshaller.setSchema(schema);
            return unmarshaller.unmarshal(in);
        } catch (JAXBException ex) {
            throw new InternalServerErrorException();
        }
    }  
}

注意Providers我没有使用它,但您可能想检查Providers中已存在的上下文。在跳转创建一个新的之前。如果没有,那么您可以创建一个新的。

所以有了这个MessageBodyReader ,您的 JAX-RS 资源方法是否接受 Domains ( <domains> ) 或 Domain ( <domain> ),它将通过此​​读取器,并将其发送到正确的资源方法。

关于java - 如何使用 MessageBodyReader 读取对象集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26800314/

相关文章:

java.lang.ExceptionInInitializerError | java.lang.ExceptionInInitializerError | java.lang.ExceptionInInitializerError由 : com. b.a.c.b 引起: 'com.b.a.b.a' 需要 'sequence' 属性

java,用鼠标角度旋转BufferedImage

web-services - GET 休息服务中的 DELETE 操作

javascript - 如何从 SAPUI5 中的 REST 端点获取模型

java - JAXB `beforeMarshal(Marshaller)` 方法应该返回什么?

java - 单例设计模式-Java

java - 无法对泛型子类进行静态引用 (Java)

web-services - RESTful 枚举。字符串还是ID?

java - @XmlEnumValue 的 jackson jaxb 注释

java - 将 XML 转换为属于同一模式的不同版本且稍有不同的 XML