【问题标题】:Jaxb2Marshaller in StaxEventItemReader causing UnmarshalExceptionStaxEventItemReader 中的 Jaxb2Marshaller 导致 UnmarshalException
【发布时间】:2014-12-15 20:37:57
【问题描述】:

所以我试图解析一个 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

知道这是什么意思吗?

当我像这样稍微更改输入xml的maintenanceTransaction开始标记时:

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

确实有效。但这不是一个解决方案,因为客户端不会像那样提供输入 xml。那么为什么会发生错误呢?确定维护事务属于哪个类似乎存在问题。

还在 marshaller bean 定义上尝试了不同的方法:

<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”,本地:“maintenanceTransaction”)。预期元素是 http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02}AccessTokenLifecycleStatus> ...这里是包中类的完整列表。

但是 xml 对 xsd 完全有效,因为这是事先完成的。想法、建议……我可以试试吗?

【问题讨论】:

    标签: jaxb spring-batch stax


    【解决方案1】:

    在从 XML 模式生成的模型上创建 JAXBContext 时,您应该执行以下操作之一来获取所有必要的元数据:

    选项 #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>
    

    【讨论】:

    • 我尝试了一次 contextPath 方法,但随后出现类似错误:[javax.xml.bind.UnmarshalException: unexpected element (uri:"pwx.com/Interface/CRS/MaintenanceTransaction_v02", local:"maintenanceTransaction")。预期的元素是 pwx.co> 这里是包中所有类的列表。
    • 也尝试了 ObjectFactory 方法,结果与 contextPath 相同。
    • @Juru - 至少这是一个更好的例外。您能否更新您的问题以包含它?
    • 已将其添加到问题中。
    • 当我在编组器定义中指定架构时,我得到“由:org.xml.sax.SAXParseException:cvc-elt.1:找不到元素'MaintenanceTransaction:maintenanceTransaction'的声明” 。”
    【解决方案2】:

    因此,在研究了代码和调试以及更多的工作之后,我最终得到了一些可行的东西。我不知道为什么,因为这是一个猜测,但它确实有效。

    <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 用作 classesToBeBound(但必须注意,它也可以工作,但在我看来这更具可读性)。真正解决它的是添加一个 mappedClass。

    根据我在调试时在代码中看到的内容(我无法检查代码 f UnmarshallingImpl,因为这是 rt.jar 的一部分,即使它是 jre 的一部分也不公开...) classesToBeBound 参数用于为 unmarshaller 创建 jaxbContext,并且 mappedClass 作为预期类型传递给此 unmarshaller。但是当没有提供这个并且 ObjectFactory 被用作参数时,错误清楚地显示了一个预期类的列表,并且存在一个适用的类......也许是解组器中的一个错误,也许是我不明白的东西。但是我的根本问题解决了。

    【讨论】:

      【解决方案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 的全局元素添加到您的架构中
      • (OR) 自定义您的架构,为MaintenanceTransaction 生成额外的@XmlRootElement
      • (或)不要使用fragmentRootElementName,解组provideCommunicationEvents_BatchRequest
      • (或)保持原样与mappedClass

      如果您在这里处理特定情况,即maintenanceTransaction,那么我会使用fragmentRootElementName/mappedClass 组合。就像你现在做的那样。

      【讨论】:

      • maintenanceTransaction之所以为fragmentRootElementName是因为我不需要包装类。我只需要将维护事务一一解析,每个维护事务都需要由spring batch的处理器处理。如果我将provideCommunicationEvents_BatchRequest 作为fragmentRootElementName,那么我将被一个需要一次性处理的大元素所困。当我打开并行处理时不是很理想,因为它只是一个原子动作。 1 个读取 1 个进程。
      • 我不明白的一件事是(当我没有指定 mappedClass 时)“预期的元素是(无)。”不是必须是“预期元素是 (uri:"pwx.com/Interface/CRS/MaintenanceTransaction_v02", local:"provideCommunicationEvents_BatchRequest")”吗?
      • 我不明白的另一件事是,当我将 ObjectFactory 而不是 MaintenanceTransaction 指定为 classesToBeBound 时,错误显示:Unexpected element (uri:"ing.com/Interface/CRS/MaintenanceTransaction_v02", local:"maintenanceTransaction ") 'Expected elements are ... ,ing.com/Interface/CRS/MaintenanceTransaction_v02}MaintenanceTransaction>,...' 这对我来说至少是相同的元素,除了我的是一个实例而预期的元素是类。
      • @Juru 如果你在你的上下文中有它应该是。如果你明确地将它用作绑定类,或者你指定一个包含 @XmlRootElement-annotated 类的上下文路径,那就是这样。
      • 包装类没有@XmlRootElement。我很清楚,这并不总是生成,取决于它是如何定义的。所以这确实可以解释为什么它不在列表中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-31
      • 2013-10-13
      • 1970-01-01
      • 2014-09-08
      • 2021-10-25
      • 2011-07-03
      • 1970-01-01
      相关资源
      最近更新 更多