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

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

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

[org.xml.sax.SAXParseException: cvc-elt.1: не удается найти объявление элемента «Подписчик».]

Я уже проверил 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>

person Randall Kwiatkowski    schedule 17.08.2011    source источник
comment
Не могли бы вы показать нам файл схемы? По сути, вы проверяете только фрагменты вашего XML, а не весь документ. Вы проверили весь документ с помощью Валидатора? Если единственное объявление элемента в схеме относится к корневому узлу, то вполне логично, что он не может найти объявление для подписчика.   -  person G_H    schedule 17.08.2011
comment
То, что вы сказали, имело смысл... Я отредактирую схему и попробую.   -  person Randall Kwiatkowski    schedule 17.08.2011


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

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

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

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

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

person G_H    schedule 18.08.2011