Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validate XML against multiple arbitrary schemas

Consider an XML document that starts like the following with multiple schemas (this is NOT a Spring-specific question; this is just a convenient XML doc for the example):

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://cxf.apache.org/jaxrs
               http://cxf.apache.org/schemas/jaxrs.xsd
           http://www.springframework.org/schema/osgi
               http://www.springframework.org/schema/osgi/spring-osgi.xsd">

I want to validate the document, but I don't know in advance which namespaces the document author will use. I trust the document author, so I'm willing to download arbitrary schema URLs. How do I implement my validator?

I know that I can specify my schemas with a DocumentBuilderFactory instance my calling setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", new String[] {...}) but I don't know the schema URLs until the document is parsed.

Of course, I could extract the XSD URLs myself after parsing the document and then running it through the validator specifying the "http://java.sun.com/xml/jaxp/properties/schemaSource" as above, but surely there's already an implementation that does that automatically?

like image 779
Chris Dolan Avatar asked Dec 28 '22 13:12

Chris Dolan


2 Answers

I haven't confirmed this but you might find Use JAXP Validation API to create a validator and validate input from a DOM which contains inline schemas and multiple validation roots useful.

In particular,

factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking);

factory.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations);
like image 112
Eugene Yokota Avatar answered Jan 05 '23 05:01

Eugene Yokota


Forgive me for answering my own question... The other answers from @Eugene Yokota and @forty-two were VERY helpful, but I thought they were not complete enough to accept. I needed to do additional work to compose the suggestions into the final solution below. The following works perfectly under JDK 1.6. It does not have sufficient error checking (see the link in Eugene's answer that is a very complete solution -- but is not reusable) nor does it cache the downloaded XSDs, I believe. I think it exploits specific features of the Xerces parser, because of the apache.org feature URLs.

    InputStream xmlStream = ...

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    factory.setValidating(true);
    factory.setXIncludeAware(true);
    factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
    factory.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
    factory.setFeature("http://apache.org/xml/features/honour-all-schemaLocations", true);
    factory.setFeature("http://apache.org/xml/features/validate-annotations", true);
    factory.setFeature("http://apache.org/xml/features/generate-synthetic-annotations", true);

    DocumentBuilder builder = factory.newDocumentBuilder();
    builder.setErrorHandler(new ErrorHandler() {
        public void warning(SAXParseException exception) throws SAXException {
            LOG.log(Level.WARNING, "parse warn: " + exception, exception);
        }
        public void error(SAXParseException exception) throws SAXException {
            LOG.log(Level.SEVERE, "parse error: " + exception, exception);
        }
        public void fatalError(SAXParseException exception) throws SAXException {
            LOG.log(Level.SEVERE, "parse fatal: " + exception, exception);
        }
    });

    Document doc = builder.parse(xmlStream);
like image 41
Chris Dolan Avatar answered Jan 05 '23 03:01

Chris Dolan