Search code examples
jaxbmockitopowermockito

JAXB complains about a namespace being present when it should be there


I have a set of unit tests that unmarshal a stored XML file into JAXB objects. They were working. These tests were using PowerMock (1.6.6) because of one detail in the CUT (code under test) that required a "whenNew" clause.

I decided to make a change to abstract that behind an object factory, which allowed me to move to pure Mockito.

When I did that, I started getting inexplicable problems when unmarshalling XML files into JAXB objects, during the test. It was simply not storing certain elements into the JAXB objects.

The method that does the unmarshalling looks like this:

protected JAXBElement<?> unmarshallToObject(Node node, Class<?> nodeClassType) throws ServiceException {
    try {
        return getUnmarshaller().unmarshal(node, nodeClassType);
    } catch (JAXBException ex) {
        baseLogger.error(null, ex, "JAXB Exception while un-marshalling");
        throw new ServiceException(UslErrCodes.BACKEND_APP.createServiceErr(BackendConfig.USL.getId(),
                "JAXB Exception while un-marshalling"), ex);
    }
}

I changed it to the following to get more information:

protected JAXBElement<?> unmarshallToObject(Node node, Class<?> nodeClassType) throws ServiceException {
    try {
        Unmarshaller unmarshaller = getUnmarshaller();
        unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
        return unmarshaller.unmarshal(node, nodeClassType);
    } catch (JAXBException ex) {
        baseLogger.error(null, ex, "JAXB Exception while un-marshalling");
        throw new ServiceException(UslErrCodes.BACKEND_APP.createServiceErr(BackendConfig.USL.getId(),
                "JAXB Exception while un-marshalling"), ex);
    }
}

What I found was that the unmarshalling process was refusing to store some elements in the JAXB instances because it was getting xml schema errors.

For instance, after changing this code, I saw messages like the following. I refer here to namespace "http://namespace1.xsd", which obviously wasn't it's original name, but all the places where that specific namespace are shown in these examples, I use this "namespace1.xsd" name.

JAXB Exception while un-marshalling:unexpected element (uri:"http://namespace1.xsd", local:"securityFeeRequired"). Expected elements are <{}securityFeeRequired>,<{}proprietarySegmentFlag>,<{}Treatment>,<{}creditScoreMessage>,<{}ServiceEligibility>,<{}warningMessage>,<{}creditScoreResult>,<{}creditBand>

As you can see, the XML document stated that the element is in a namespace, but the "{}" seems to imply that no namespace was expected.

This is an excerpt from the generated JAXB class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "creditScoreResult", "creditScoreMessage", "warningMessage",
        "securityFeeRequired", "treatment", "creditBand", "proprietarySegmentFlag", "serviceEligibility" })
public static class FooResult implements Serializable {
    private static final long serialVersionUID = 1L;
    protected String creditScoreResult;
    protected String creditScoreMessage;
    protected String warningMessage;
    protected boolean securityFeeRequired;

And here is the @XmlSchema annotation from the generated "package-info" from the same package:

@XmlSchema(namespace="http://namespace1.xsd", elementFormDefault=XmlNsForm.QUALIFIED)

So, after getting this error, I decided to try making a "this couldn't possibly work" change.

I changed the XML file such that this is the analogous excerpt:

    <ExecuteFooResponse xmlns:ns1="http://namespace1.xsd" xmlns:ns2="http://namespace2.xsd">
        <FooResult>
            <securityFeeRequired>false</securityFeeRequired>
            <Treatment><code>A001</code>
                <message>No additional fee is required at this time</message>
            </Treatment>
            <ServiceEligibility>
                <productCode>BAR</productCode>
                <serviceEligibilityIndicator>true</serviceEligibilityIndicator>
            </ServiceEligibility>
        </FooResult>
        <Response>
            <ns2:code>0</ns2:code>
            <ns2:description>Success</ns2:description>
        </Response>
    </ExecuteFooResponse>

The only change I made was the namespace prefix for "namespace1.xsd". It was failing with "" (blank), which is correct, as far as I can see. I changed it to "ns1", but only in the declaration. I'm clearly not referencing that prefix in the body of the element. This makes the test PASS.

Please restore my sanity.


Solution

  • The problem ended up being that the XML documents the tests were using were actually not schema-valid. They had subtle issues that caused validation to fail, but those issues were place there intentionally by other developers because they found that the PowerMock tests would not pass without those ERRORS being in place. I'm still trying to figure out why PowerMock would only work with the files in that state, but my documents are now schema-valid, and the Mockito tests are working.