Java 针对多个任意模式验证XML

Java 针对多个任意模式验证XML,java,xml,validation,xsd,Java,Xml,Validation,Xsd,考虑一个XML文档,该文档以如下方式开始,具有多个模式(这不是Spring特有的问题;这只是一个方便的XML文档示例): 我想验证文档,但我事先不知道文档作者将使用哪些名称空间。我信任文档作者,因此我愿意下载任意模式URL。我如何实现我的验证器 我知道我可以通过调用setAttribute(“http://java.sun.com/xml/jaxp/properties/schemaSource“,新字符串[]{…})但在解析文档之前,我不知道架构URL 当然,在解析文档并通过指定的验证器运

考虑一个XML文档,该文档以如下方式开始,具有多个模式(这不是Spring特有的问题;这只是一个方便的XML文档示例):


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

我知道我可以通过调用
setAttribute(“http://java.sun.com/xml/jaxp/properties/schemaSource“,新字符串[]{…})
但在解析文档之前,我不知道架构URL


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

我还没有确认这一点,但您可能会发现它很有用

特别是,

factory.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking);

factory.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations);

如果您像这样创建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
参数中


我认为构建器会自动执行此操作,而无需指定解析器,但显然不是现成的。它可能是由其他功能、属性或属性控制的?

请原谅我回答了我自己的问题。。。来自@Eugene Yokota和@四十二的其他答案非常有用,但我认为它们不够完整,无法接受。我需要做额外的工作,将建议组合到下面的最终解决方案中。以下内容在JDK1.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);

新读者概要:该链接的示例代码对XML进行非验证性解析,然后使用XPath查找根节点中指定的模式,然后使用模式验证器。我想这是可行的,但它很难看,而且肯定有人已经以库的形式写过了,是吗?请注意
setValidating(true)
会导致XSD验证。看见
    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);