Как я могу проверить свой XML, используя схему, считывая только один объект / элемент за раз XMLStreamReader JAXB? - PullRequest
3 голосов
/ 17 августа 2011

Приведенный ниже код работает правильно, чтобы демонтировать XML из потока по одному объекту за раз.

Но когда я отменяю комментарий в строке unmarshaller.setSchema(schema), программа выдает исключение:

[org.xml.sax.SAXParseException: cvc-elt.1: не удается найти объявление элемента 'Subscriber'.]

Я уже проверил XML с использованием класса javax.xml.validation.Validator, но моя цель состоит в том, чтобы проверять и демаршировать одновременно, по одному элементу за раз.

Это мой текущий код:

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 Ответ

1 голос
/ 18 августа 2011

Попробуйте с такой схемой:

<?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 знает класс для SubscriberType и SubscriberInformation. Если вы дадите ему XML-документ с корневым элементом <Subscribers>, он знает, что он должен разархивировать класс для SubscriberType. Однако, если вы дадите ему XML-документ с корневым элементом <Subscriber>, он обычно не найдет определение этого элемента в классе ObjectFactory, созданном XJC. Но так как вы использовали метод unmarshal, который принимает второй аргумент, а именно класс, который вы ожидаете, вы сказали демаршаллеру, что он должен интерпретировать свой ввод как SubscriberType. Результатом будет пустой SubscriberType экземпляр.

Теперь, поскольку вы перебираете элементы <Subscriber> один за другим (по крайней мере, я так понимаю, вы собираетесь это сделать), для unmarshaller кажется, что он получает XML-документы с этим именем root элемент. Он не будет жаловаться на то, что не нашел это определение, поскольку вы решили выяснить тип с помощью аргумента класса. Но в тот момент, когда вы присоединяете схему для проверки, все выходит из строя. Валидатор не знает, что вы находитесь внутри элемента <Subscribers>. Он ожидает полный документ XML. Таким образом, он ищет объявление элемента для <Subscriber>, но получается пустым, поскольку этот элемент определен только в сложном типе. Это не глобальное определение элемента (то есть одно под корнем схемы).

Итак, две вещи здесь. Один из них - определить элемент <Subscriber>, как показано выше, а затем ссылаться на него в ваших сложных типах. Другой способ - изменить свой не маршальный вызов на unmarshal(streamReader, SubscriberInformation.class), чтобы вернуть правильный тип объекта. Также обратите внимание на наличие бесконечных циклов или неверного демаршаллинга, поскольку ваш вызов streamReader.next() находится в состоянии и может не сработать.

Написание схем с учетом JAXB требует определенного стиля. В общем, лучше всего определять элементы глобально, а затем ссылаться на них. Определите элемент локально внутри сложного типа, только если он обязательно должен оставаться там инкапсулированным.

Извините за многословный ответ, я не очень хорошо проснулся:)

...