Jaxb2Marshaller in StaxEventItemReader throws UnmarshalException

So I am trying to parse the xml and decouple it into a program. This is an example 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 is defined as:

<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>

      

With the following setup my StaxEventItemReader and 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>

      

However, the problem is that I am getting the following exception.

 * [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

      

Any idea what this is about?

When I slightly changed the maintenanceTransaction maintenance opening tag of the input xml as follows:

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

      

He works. But this is not a solution, as clients will not be delivering input XML. So why does the error occur? There seems to be a problem with determining which class the service is supported in.

Also tried different approaches for defining the bean 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>

      

and

<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>

      

The results for both are the same and similar to the first error

javax.xml.bind.UnmarshalException: Unexpected element (uri: " http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02 ", local: "maintenanceTransaction"). Expected items: <{ http://www.pwx.com/Interface/CRS/MaintenanceTransaction_v02 } AccessTokenLifecycleStatus> ... and here is the entire list of classes in the package.

But xml works great against xsd as it is done beforehand. Ideas, suggestions, ... what could I try?

+3


source to share


3 answers


So after learning the code and debugging and a lot more work, I ended up with something that works. I don't know why as this was speculation, but it works.

<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>

      



As you can see there is no need to use ObjectFactory as classesToBeBound (but it should be noted that this would work, but in my opinion it is more readable). What really fixes this is to add the associated class.

From what I saw in the code while debugging (I couldn't check the f UnmarshallingImpl code as it is part of rt.jar and it is not publicly available even though it is part of jre ...) The classesToBeBound parameter is used to create the jaxbContext for unmarshaller, and the mappedClass is passed to that unmarshaller as expectedType. But when not provided and ObjectFactory is used as a parameter, the error explicitly displays the list of expected classes, and the one that is applicable is present ... Maybe a bug in unmarshaller, maybe something that I don't understand. But my root problem has been resolved.

+4


source


Here's my theory.

What it really does mappedClass

is the unmarshalling method from unmarshaller.unmarshal(source)

to unmarshaller.unmarshal(source, this.mappedClass)

. This is called "partial unmarshalling", i.e. You can decouple a well-known class from any element regardless of the element name.

So, as you say, it works for you. And it's not without mappedClass

. This means that you are missing an element declaration for your root element. This also fits well with the error message:

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

      

Which says basically the same thing.

This is also correct. Since your schema does not declare a global element for maintenanceTransaction

, only for provideCommunicationEvents_BatchRequest

.

So, basically you are decoupling the wrong item. And since your schema doesn't have a declaration for this, it fails. But if you specify exactly what type you want (thus providing the declaration "explicitly"), then it works.



Then the question arises: why are you unwinding the wrong element. I guess it's because you have

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

      

This probably indicates the unmarshaller is using the element maintenanceTransaction

as the root element. So unmarshaller is being applied to the wrong item.

Using contextPath

, adding more related classes, etc. does not help as neither of them add an element declaration for maintenanceTransaction

.

Now how to fix it. I see the following options:

  • Add a global element for maintenanceTransaction

    to your schema
  • (OR) Customize your schema to create additional @XmlRootElement

    formaintenanceTransaction

  • (OR) Don't use fragmentRootElementName

    , unmarshalprovideCommunicationEvents_BatchRequest

  • (OR) Leave it as it is with mappedClass

If you are handling the specific case here, namely maintenanceTransaction

, I would use the combination fragmentRootElementName

/ mappedClass

. As it is now.

+5


source


When building JAXBContext

on a model generated from XML schema, you must do one of the following to get all the required metadata:

Option # 1 - Create it in the created ObjectFactory

class

<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>

      

Option # 2 - Create it in the generated model package names

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

      

0


source







All Articles