针对多个任意模式验证XML

时间:2011-10-03 17:24:55

标签: java xml validation xsd

考虑一个XML文档,其开头如下所示,具有多个模式(这不是特定于Spring的问题;这只是一个方便的XML文档):

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

我想验证文档,但我事先并不知道文档作者将使用哪些名称空间。我相信文档作者,所以我愿意下载任意模式URL。如何实现验证器?

我知道我可以使用我调用setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", new String[] {...})的DocumentBuilderFactory实例指定我的模式,但在解析文档之前我不知道模式URL。

当然,我可以在解析文档之后自己提取XSD URL,然后通过验证器运行它,如上所述指定"http://java.sun.com/xml/jaxp/properties/schemaSource",但是肯定已经有一个实现自动完成了吗?

3 个答案:

答案 0 :(得分:4)

我还没有确认这一点,但您可能会发现Use JAXP Validation API to create a validator and validate input from a DOM which contains inline schemas and multiple validation roots有用。

特别是

factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking);

factory.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations);

答案 1 :(得分:4)

请原谅我回答我自己的问题...来自@Eugene Yokota和@ forty-two的其他答案非常有帮助,但我认为他们不够完整,无法接受。我需要做更多的工作,将建议写入下面的最终解决方案。以下在JDK 1.6下完美运行。它没有足够的错误检查(请参阅Eugene的答案中的链接,这是一个非常完整的解决方案 - 但不可重复使用),我相信它也不会缓存下载的XSD。我认为它利用了Xerces解析器的特定功能,因为apache.org功能URL。

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

答案 2 :(得分:2)

如果你像这样创建一个DocumentBuilderFactory:

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(true);
    dbf.setNamespaceAware(true);
    dbf.setAttribute(
            "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
            "http://www.w3.org/2001/XMLSchema");

然后,您可以在此工厂创建的EntityResolver实例上设置DocumentBuilder,以便有机会解析指令中引用的架构位置。指定的位置将出现在systemId参数中。

我认为构建器会自动执行此操作,而不指定解析器,但显然不是开箱即用的。可能是由另一个功能,属性或属性控制?