简体   繁体   中英

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" ) );
}

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 ) );
    }
}

The matching wildcard is strict, but no declaration can be found for element 'b:person'.

This message says that any element is declared with processContents="strict" (which is default) in your schema. The validator is expected to process the content of any element but the element declaration b:person is missing.

There are two ways to handle the situation:

  1. Make sure the set of schema provided to validator includes b:person element declaration - it is possible to provide more than one schema source to SchemaFactory.newSchema . Validator will validate the full XML in this case, including the contents of any elements.

  2. OR - Relax content processing rules by specifying attribute processContents="lax" or processContents="skip" in you schema for any element declaration in order to perform partial validation as described at XML Schema any Element . Validator will NOT validate the full XML in this case, skipping the contents of any elements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM