如何使用模式验证我的XML,JAXB的XMLStreamReader一次只能读取一个对象/元素?

时间:2011-08-17 16:56:43

标签: java xml jaxb stax

下面的代码可以正常地从一个对象的流中解析XML。

但是当我取消评论unmarshaller.setSchema(schema)行时,该程序会抛出异常:

  

[org.xml.sax.SAXParseException:cvc-elt.1:找不到元素'Subscriber'的声明。]

我已经使用javax.xml.validation.Validator类验证了XML,但我的目标是同时验证和解组,一次一个元素。

这是我目前的代码:

SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); 
Schema schema = sf.newSchema(new File("/Path to xsd"));

XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new FileReader("/Path to xml"));

JAXBContext jaxbContext = JAXBContext.newInstance(SubscriberType.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
//unmarshaller.setSchema(schema);

streamReader.nextTag();
streamReader.require(XMLStreamConstants.START_ELEMENT, null, "Subscribers");
streamReader.nextTag();    
while (streamReader.getEventType() == XMLStreamConstants.START_ELEMENT) {

    JAXBElement<SubscriberType> pt = unmarshaller.unmarshal(streamReader, SubscriberType.class);
    //do something with the unmarshalled object pt...store to db ect.

    if (streamReader.getEventType() == XMLStreamConstants.CHARACTERS) {
        streamReader.next();
    }
}

我的架构subscriber.xsd的摘录:

<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        elementFormDefault="unqualified" 
        attributeFormDefault="unqualified">

  <xsd:element name="Subscribers" type="SubscriberType" />

  <xsd:complexType name="SubscriberType">
    <xsd:sequence>
      <xsd:element name="Subscriber" 
              type="SubscriberInformation" 
              minOccurs="1" 
              maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>

1 个答案:

答案 0 :(得分:1)

尝试使用这样的架构:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified" attributeFormDefault="unqualified">

    <xsd:element name="Subscribers" type="SubscriberType"/>

    <xsd:element name="Subscriber" type="SubscriberInformation" />

    <xsd:complexType name="SubscriberType">
        <xsd:sequence>
            <xsd:element ref="Subscriber" minOccurs="1" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>

我相信你的架构会发生这样的事情:JAXB上下文知道SubscriberTypeSubscriberInformation的类。如果你要给它一个带有<Subscribers>根元素的XML文档,它就知道它必须为SubscriberType的类解组。但是如果你给它一个带有<Subscriber>根元素的XML文档,它通常不会在XJC生成的ObjectFactory类中找到这个元素定义。但是既然你已经使用了unmarshal方法来接受第二个参数,即你期望的类,你已经告诉unmarshaller它应该将它的输入解释为SubscriberType。结果将是一个空的SubscriberType实例。

现在,既然你逐个遍历<Subscriber>个元素(至少这是我收集你的意思),对于unmarshaller来说,似乎它正在接收带有该元素的XML文档作为根元素。它不会抱怨没有找到那个定义,因为你已经完成了使用类参数计算类型的任务。但是,当您附加架构进行验证时,事情就会崩溃。验证器不知道您在<Subscribers>元素内。它期待一个完整的XML文档。因此,它会查找<Subscriber>的元素声明,但会显示为空,因为该元素仅在复杂类型中定义。它不是全局元素定义(即模式根目录下的定义)。

所以,这里有两件事要做。一种是定义元素<Subscriber>,如上所示,然后在复杂类型中引用它。另一种方法是将您的解组调用更改为unmarshal(streamReader, SubscriberInformation.class)以获取正确类型的对象。同时注意无限循环或不正确的解组,因为您对streamReader.next()的调用处于某种状态并且可能无法触发。

使用JAXB编写模式需要一定的风格。通常,最好全局定义元素,然后引用它们。只有在绝对必须保持封装的情况下才在复杂类型中本地定义元素。

对不起啰嗦的答案,我不太清醒:)