如何使用MessageBodyReader读取对象集合

时间:2014-11-07 11:33:01

标签: java rest jaxb xsd jax-rs

我有一些类型的XSD Schema,让我们把它命名为A.

然后我尝试使用XSD Schema验证编写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;
}

1 个答案:

答案 0 :(得分:1)

所以这就是事情。 XML文档必须具有根元素,因此您无法发送类似

的内容
<domain></domain>
<domain></domain>

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

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

话虽如此,xsd架构还需要反映其他根元素。您无法定义<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>),它将通过这个阅读器,并将其发送到正确的资源方法。