Marcus Junius Brutus Marcus Junius Brutus - 3 months ago 22
Java Question

JAXB2, how to partially validate against an XML Schema that includes an xsd:any element?

Is it possible in JAXB to only validate, during the unmarshalling, up to an xsd:any element and drill no further?

The use case is that I have two XML Schema files, one defining the envelope (which includes at some point an xsd:any element) and another one defining the payload schema (for the xsd:any element). Since actually there can be many different types of payload - with more to come in the future - I've built my code to use two-step unmarshalling as suggested in the answer to this SO question.

So I have a library that only unmarshalls and validates the envelope without looking at the payload. The thing is that using JAXB I can't figure a way to only validate the envelope using the

setSchema
method of the
Unmarshaller
. The validation fails because it can't find the schema of the payload, but I can't add these schemas in the envelope-handling jar since the envelope is supposed to be agnostic of the payloads. So I am not able to implement the envelope-handling logic in its own library in a manner agnostic of the payloads.

A minimal, concrete example follows. When the code runs it fails with:

[java] Caused by: org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 23; cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'b:person'.


envelope - A.XSD



<?xml version="1.0" encoding="UTF-8"?>
<schema elementFormDefault="qualified"
xmlns ="http://www.w3.org/2001/XMLSchema"
xmlns:a ="http://www.example.org/A"
targetNamespace ="http://www.example.org/A">
<element name="someType" type="a:SomeType"></element>
<complexType name="SomeType">
<sequence>
<any namespace="##other" processContents="strict"/>
</sequence>
</complexType>
</schema>


payload - B.XSD



<?xml version="1.0" encoding="UTF-8"?>
<schema elementFormDefault="qualified"
xmlns ="http://www.w3.org/2001/XMLSchema"
xmlns:b ="http://www.example.org/B"
targetNamespace="http://www.example.org/B"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<element name="person" type="b:PersonType"></element>
<complexType name="PersonType">
<sequence>
<element name="firstName" type="string"/>
<element name="lastName" type="string"/>
</sequence>
</complexType>
</schema>


XML instance - ab.xml



<?xml version="1.0" encoding="UTF-8"?>
<a:someType xmlns:a="http://www.example.org/A"
xmlns:b="http://www.example.org/B"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/A A.xsd
http://www.example.org/B B.xsd">
<b:person>
<b:firstName>Mary</b:firstName>
<b:lastName>Bones</b:lastName>
</b:person>
</a:someType>


JAXB code



public static void main (String args[]) throws JAXBException, FileNotFoundException, SAXException {
final Schema SCHEMA_A = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new File("A.xsd"));
JAXBContext jc = JAXBContext.newInstance("example.a");
Unmarshaller u = jc.createUnmarshaller();
u.setSchema(SCHEMA_A);
JAXBElement<SomeType> element = (JAXBElement<SomeType>) u.unmarshal( new FileInputStream( "ab.xml" ) );
}

Answer

I was not able to achieve this kind of partial validation with JAXB. However using javax.xml.validation.Validator I was to validate the outer envelope and suppress the exception when the payload element is reached with a custom org.xml.sax.ErrorHandler. The solution is not satisfactory at all as it relies on a comparison of the exception message in order to suppress the exception (the getColumnNumber and getLineNumber methods of the SAXParseException class cannot be used as the exact line and column where the error occurs are not fixed XML instance documents).

I paste the code below, but like I said I don't like the message comparison suppression logic at all as I have to anticipate all possible any payload elements (since the element name appears in the message). Is there a better way?

class CustomErrorHandler implements ErrorHandler {

    @Override
    public void warning(SAXParseException exc) {
        throw exc;
    }

    @Override
    public void error(SAXParseException exc) throws SAXParseException {
        if (exc.getMessage().equals("cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'b:person'."))
            ; // suppress
        else
            throw exc;
    }

    @Override
    public void fatalError(SAXParseException exc) throws SAXParseException {
        throw exc;
    }
}

public class FooMain {


    public static void main (String args[]) throws JAXBException, FileNotFoundException, SAXException, IOException  {
        String xmlFileName = "ab.xml";
        final Schema SCHEMA_A = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new File("A.xsd"));
        SAXSource source = new SAXSource(new InputSource(xmlFileName));
        Validator validator = SCHEMA_A.newValidator();
        validator.setErrorHandler(new CustomErrorHandler());
        validator.validate(source);
        JAXBContext jc = JAXBContext.newInstance("example.a");
        Unmarshaller u = jc.createUnmarshaller();
        // u.setSchema(SCHEMA_A); // not possible to partially validate using this method
        JAXBElement<SomeType> element = (JAXBElement<SomeType>) u.unmarshal( new FileInputStream( xmlFileName ) );
    }
}
Comments