Jaxb2Marshaller в StaxEventItemReader, вызывающий UnmarshalException

Итак, я пытаюсь разобрать xml и преобразовать его в программу. Это пример xml:

<MaintenanceTransaction:provideCommunicationEvents_BatchRequest
        xmlns:MaintenanceTransaction="http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <MaintenanceTransaction:maintenanceTransaction>
        <MaintenanceTransaction:eventInitiatedBy>
            <MaintenanceTransaction:identifier>FINACLE</MaintenanceTransaction:identifier>
        </MaintenanceTransaction:eventInitiatedBy>
        <MaintenanceTransaction:transactionDate>2014-06-21T19:00:32.356+01:00</MaintenanceTransaction:transactionDate>
        <MaintenanceTransaction:maintenanceTransactionType>PRE-NOTIFICATION
        </MaintenanceTransaction:maintenanceTransactionType>
        <MaintenanceTransaction:maintenanceEntries>
            <MaintenanceTransaction:hasNewValues xsi:type="MaintenanceTransaction:DepositArrangement">
                <MaintenanceTransaction:enterpriseId xsi:type="MaintenanceTransaction:ArrangementIdentifier">
                    <MaintenanceTransaction:identifier>222000000322</MaintenanceTransaction:identifier>
                    <MaintenanceTransaction:enterpriseIdType>Term Deposit</MaintenanceTransaction:enterpriseIdType>
                </MaintenanceTransaction:enterpriseId>
                <MaintenanceTransaction:maturityDate>2014-10-08</MaintenanceTransaction:maturityDate>
            </MaintenanceTransaction:hasNewValues>
        </MaintenanceTransaction:maintenanceEntries>
    </MaintenanceTransaction:maintenanceTransaction>
</MaintenanceTransaction:provideCommunicationEvents_BatchRequest>

XSD определяется как:

<xsd:schema xmlns:MaintenanceTransaction="http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02" elementFormDefault="qualified" attributeFormDefault="qualified">
    <xsd:include schemaLocation="./commonIFWxsd/IFWXML.xsd" />
    <xsd:include schemaLocation="./commonIFWxsd/Event.xsd" />
    <xsd:include schemaLocation="./commonIFWxsd/Arrangement.xsd" />
    <!-- end of TYPES REQUIRED FOR PARAMETERS -->
    <xsd:complexType name="provideCommunicationEvents_BatchRequest">
        <xsd:sequence>
            <xsd:element name="maintenanceTransaction" type="MaintenanceTransaction:MaintenanceTransaction" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    <!-- TYPES REQUIRED FOR PARAMETERS -->
    <xsd:element name="provideCommunicationEvents_BatchRequest" type="MaintenanceTransaction:provideCommunicationEvents_BatchRequest" />
</xsd:schema>

При следующей настройке моих StaxEventItemReader и Jaxb2Marshaller:

<bean id="uploadEventMessageReader" parent="abstractUploadEventMessageReader" scope="step">
    <property name="resource" value="file:#{jobExecutionContext['fileToProcess']}"/>
    <property name="fragmentRootElementName" value="maintenanceTransaction"/>
    <property name="unmarshaller" ref="maintenanceTransactionUnmarshaller"/>
</bean>

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" scope="step">
    <property name="classesToBeBound">
        <list>
            <value>com.pwx.crs.informationcollection.ifw.process.model.mt.MaintenanceTransaction</value>
        </list>
    </property>
</bean>

Однако проблема в том, что я получаю следующее исключение.

 * [javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02", local:"maintenanceTransaction"). Expected elements are (none)] on step uploadEventMessages, with message: uploadEventMessagesStep. 
 * --stacktrace:com.pwx.crs.frwk.exp.technical.CRS2UnexpectedBatchException: An unexpected exception occurred in batch job JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException

Есть идеи, о чем идет речь?

Когда я немного изменяю открывающий тег MaintenanceTransaction входного xml следующим образом:

<MaintenanceTransaction:maintenanceTransaction xsi:type="MaintenanceTransaction:MaintenanceTransaction">

Это работает. Но это не решение, поскольку клиенты не будут доставлять входные XML-файлы таким образом. Так почему же возникает ошибка? Кажется, есть проблема с определением того, частью какого класса является MaintenanceTransaction.

Также пробовал разные подходы к определению бина marshaller:

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" scope="step">
    <property name="contextPath" value="com.pwx.crs.informationcollection.ifw.process.model.mt"/>
</bean>

и

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" scope="step">
    <property name="classesToBeBound">
        <list>
            <value>com.pwx.crs.informationcollection.ifw.process.model.mt.ObjectFactory</value>
        </list>
    </property>
</bean>

Результаты для обоих одинаковы и похожи на первую ошибку

javax.xml.bind.UnmarshalException: неожиданный элемент (uri:"http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02", local:"maintenanceTransaction"). Ожидаемые элементы: ‹{http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02}AccessTokenLifecycleStatus> ... а тут весь список классов в пакете.

Но xml совершенно совместим с xsd, так как это делается заранее. Идеи, предложения, ... что я мог бы попробовать?


person Juru    schedule 20.10.2014    source источник


Ответы (3)


Вот моя теория.

На самом деле mappedClass переключает метод десортировки с unmarshaller.unmarshal(source) на unmarshaller.unmarshal(source, this.mappedClass). Это называется «частичным демаршалированием», т. е. вы можете демаршалировать известный класс из какого-то элемента независимо от имени элемента.

Так что, как вы говорите, это работает для вас. И не без mappedClass. Это будет означать, что вам не хватает объявления элемента для вашего корневого элемента. Это также хорошо согласуется с ошибкой, о которой вы сообщили:

javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02",
local:"maintenanceTransaction"). Expected elements are (none)

Что говорит в основном о том же.

Это также правильно. Поскольку ваша схема не объявляет глобальный элемент для maintenanceTransaction, только для provideCommunicationEvents_BatchRequest.

Итак, в основном вы разбираете неправильный элемент. И поскольку в вашей схеме нет объявления для этого, это не удается. Но если вы точно укажете, какой тип вы хотите (таким образом, предоставив объявление «явно»), тогда это сработает.

Далее вопрос в том, почему вы разбираете не тот элемент. Я предполагаю, что это потому, что у вас есть

<property name="fragmentRootElementName" value="maintenanceTransaction"/>

Это, вероятно, указывает демаршаллеру использовать элемент maintenanceTransaction в качестве корневого элемента. Таким образом, unmarshaller применяется не к тому элементу.

Использование contextPath, добавление дополнительных связанных классов и т. д. не помогает, так как ни один из них не добавляет объявление элемента для maintenanceTransaction.

Теперь, как это исправить. Я вижу следующие варианты:

  • Добавьте глобальный элемент для maintenanceTransaction в вашу схему
  • (ИЛИ) Настройте свою схему, чтобы создать дополнительный @XmlRootElement для MaintenanceTransaction
  • (ИЛИ) Не используйте fragmentRootElementName, отключите provideCommunicationEvents_BatchRequest
  • (ИЛИ) Оставить как есть с mappedClass

Если вы обрабатываете здесь конкретный случай, а именно maintenanceTransaction, я бы использовал комбинацию fragmentRootElementName/mappedClass. Так же, как вы делаете сейчас.

person lexicore    schedule 23.10.2014
comment
Причина, по которой MaintenanceTransaction является fragmentRootElementName, заключается в том, что мне не нужен класс-оболочка. Мне просто нужно анализировать транзакции обслуживания одну за другой, каждая транзакция обслуживания должна обрабатываться процессором весеннего пакета. Если бы я взял ProvideCommunicationEvents_BatchRequest как fragmentRootElementName, то я бы застрял с одним большим элементом, который мне нужно обработать за один раз. Не очень оптимально, когда я включаю параллельную обработку, так как это будет просто одно атомарное действие. 1 чтение 1 процесс. - person Juru; 23.10.2014
comment
Одна вещь, которую я не понимаю, это (когда я не указываю mappedClass) «Ожидаемые элементы (нет)». Разве это не должно быть «Ожидаемые элементы: (uri:pwx.com/Interface/CRS/ MaintenanceTransaction_v02, local:provideCommunicationEvents_BatchRequest)'? - person Juru; 23.10.2014
comment
И еще одна вещь, которую я не понимаю, это то, что когда я указываю ObjectFactory вместо MaintenanceTransaction как classToBeBound, появляется сообщение об ошибке: Unexpected element (uri:ing.com/Interface/CRS/MaintenanceTransaction_v02, local:maintenanceTransaction) 'Ожидаемые элементы: ... ,‹{ing.com/Interface/CRS/MaintenanceTransaction_v02}MaintenanceTransaction›,...' Что... для меня по крайней мере тот же элемент, за исключением за то, что мой - это экземпляр, а ожидаемый - класс. - person Juru; 23.10.2014
comment
@Juru Должно быть, если у вас есть это в вашем контексте. Это было бы, если бы вы использовали его как связанный класс явно или если бы вы указывали контекстный путь, который включал бы этот @XmlRootElement-аннотированный класс. - person lexicore; 23.10.2014
comment
Класс-оболочка не имеет @XmlRootElement. Мне ясно, что это не всегда генерируется, зависит от того, как это определено. Так что это действительно объясняет, почему его нет в списке. - person Juru; 23.10.2014
comment
Значит, это так. 1. Он использует mappedClass в качестве ожидаемого элемента, или 2, он использует все возможные классы в objectfactory в качестве ожидаемого элемента, или 3, он использует все элементы с @XmlRootElement в пакетах, предоставленных в качестве ожидаемых элементов. Странно только то, что он не может сопоставить экземпляр (неожиданный) MaintenanceTransaction с ожидаемым MaintenanceTransaction. - person Juru; 23.10.2014
comment
@Juru Это немного сложнее. Также есть @XmlRegistry и @XmlSeeAlso. Не стесняйтесь задавать другой вопрос по этому поводу, слишком большой для комментария. - person lexicore; 23.10.2014
comment
Нормально будет сделать. Награжу вас наградой, когда смогу (через 10 часов). Спасибо за помощь. - person Juru; 23.10.2014
comment
@Juru Спасибо за награду. :) - person lexicore; 27.10.2014
comment
Нет проблем, @lexicore, извините за задержку. - person Juru; 27.10.2014

Итак, после исследования кода, отладки и многого другого труда я получил то, что работает. Я точно не знаю, почему, так как это было предположение, но это работает.

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
    <property name="classesToBeBound">
        <list>
            <value>com.pwx.crs.informationcollection.ifw.process.model.mt.MaintenanceTransaction</value>
        </list>
    </property>
    <property name="mappedClass" value="com.pwx.crs.informationcollection.ifw.process.model.mt.MaintenanceTransaction"/>
</bean>

Как видите, нет необходимости использовать ObjectFactory как classToBeBound (но следует отметить, что это тоже сработало бы, но, на мой взгляд, это более читабельно). Что действительно исправляет это, так это добавление mappedClass.

Из того, что я видел в коде во время отладки (я не смог проверить код f UnmarshallingImpl, поскольку он является частью rt.jar и не является общедоступным, даже если он является частью jre...) Параметр classToBeBound используется для создайте jaxbContext для демаршаллера, и mappedClass будет передан этому демаршаллеру как ожидаемый тип. Но когда это не предусмотрено и в качестве параметра используется ObjectFactory, ошибка четко показывает список ожидаемых классов, и тот, который применим, присутствует ... Может быть, ошибка в unmarshaller, может быть, что-то, чего я не понимаю. Но моя основная проблема решена.

person Juru    schedule 23.10.2014

При создании JAXBContext для модели, сгенерированной из XML-схемы, вы должны выполнить одно из следующих действий, чтобы получить все необходимые метаданные:

Вариант 1. Создайте его в сгенерированном классе ObjectFactory

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" scope="step">
    <property name="classesToBeBound">
        <list>
            <value>com.pwx.crs.informationcollection.ifw.process.model.mt.ObjectFactory</value>
        </list>
    </property>
</bean>

Вариант 2. Создайте его на основе имен пакетов сгенерированной модели.

<bean id="maintenanceTransactionUnmarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller" scope="step">
    <property name="contextPath" value="com.pwx.crs.informationcollection.ifw.process.model.mt"/>
</bean>
person bdoughan    schedule 20.10.2014
comment
Однажды я попробовал подход contextPath, но затем получил аналогичную ошибку: [javax.xml.bind.UnmarshalException: неожиданный элемент (uri:pwx.com/Interface/CRS/MaintenanceTransaction_v02, local:maintenanceTransaction). Ожидаемые элементы: ‹{pwx.co › здесь список всех классов в пакете. - person Juru; 21.10.2014
comment
Также попробовал подход ObjectFactory, тот же результат, что и для contextPath. - person Juru; 21.10.2014
comment
@Juru - по крайней мере, это лучшее исключение. Вы можете обновить свой вопрос, чтобы включить его? - person bdoughan; 21.10.2014
comment
Добавил к вопросу. - person Juru; 21.10.2014
comment
Когда я также указываю схему в определении маршаллера, я получаю сообщение Причина: org.xml.sax.SAXParseException: cvc-elt.1: Не удается найти объявление элемента «MaintenanceTransaction:maintenanceTransaction». - person Juru; 21.10.2014