根据多个任意模式验证XML

4
考虑一个以以下内容开头的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。如何实现我的验证器?
我知道可以通过调用DocumentBuilderFactory实例的setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", new String[] {...})方法指定模式,但在解析文档之前,我不知道模式URL。
当然,我可以在解析文档后提取XSD URL,并通过指定上述“http://java.sun.com/xml/jaxp/properties/schemaSource”来运行它通过验证器,但肯定已经有自动完成这一过程的实现了吧?
3个回答

4

新读者摘要:该链接包含示例代码,用于对XML进行非验证解析,然后使用XPath查找根节点中指定的模式,最后使用模式验证器。我想它会起作用,但很丑陋,而且肯定已经有人以库的形式编写了这个,是吗? - Chris Dolan

4

请原谅我自问自答... @Eugene Yokota和@forty-two的其他答案非常有帮助,但我认为它们不够完整以至于无法被接受。我需要额外的工作将建议组合为下面的最终解决方案。以下代码在JDK 1.6下完美运行。它没有足够的错误检查(请参见Eugene的答案中提供的链接,该链接是一个非常完整的解决方案,但不可重用),也没有缓存下载的XSDs。我认为它利用了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);

请注意,setValidating(true) 会导致 XSD 验证。请参阅 https://docs.oracle.com/javase/7/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#setValidating(boolean)。 - koppor

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

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

我认为构建器会自动完成这个过程,而不需要指定解析器,但显然它不能直接完成。也许它受到另一个特性、属性或属性的控制?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接