【问题标题】:Parsing invalid XSD in Java在 Java 中解析无效的 XSD
【发布时间】:2016-01-03 10:08:22
【问题描述】:

我有一个无效的 XSD 文件,我正在尝试用 Java 解析它。当我尝试解析无效的XSD 时,我只收到一次SAXParseException。第二次和所有后续尝试都不会产生SAXParseException。我不希望Java有错误,但也无法理解行为,每次都期待SAXParseException。非常欢迎任何帮助或解释。

这里有一个Java源码:

package com.myxsd;

import java.io.File;
import java.io.IOException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class ParseXSD {

    public static void main(String[] args) throws IOException {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        String file = ParseXSD.class.getClassLoader().getResource("com/myxsd/myxsd.xsd").getFile();
        try {
            Schema schema = sf.newSchema(new File(file)); 
        } catch (SAXException e) {
            e.printStackTrace();
        }       

        // sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        try {      
            // no exception is thrown during second attempt to get schema
            Schema schema = sf.newSchema(new File(file)); 
            Validator validator = schema.newValidator();            
            validator.setErrorHandler(new MyErrorHandler());
            // sample.xml gets validated without any issues
            validator.validate(new StreamSource(ParseXSD.class.getClassLoader().getResource("com/myxsd/sample.xml").getFile()));
        } catch (SAXException e) {
            e.printStackTrace();
        }   
    }

    private static class MyErrorHandler implements ErrorHandler {

        private boolean isValid = true;

        public boolean isValid() {
            return this.isValid;
        }

        @Override
        public void warning(SAXParseException exc) {
            exc.printStackTrace();
        }

        @Override
        public void error(SAXParseException exc) throws SAXParseException {
            exc.printStackTrace();
            this.isValid = false;
            throw exc;
        }

        @Override
        public void fatalError(SAXParseException exc) throws SAXParseException {
            exc.printStackTrace();
            this.isValid = false;
            throw exc;
        }
    }
}

和无效的 myxsd.xsd 文件:

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:n1="www.myns.com/XMLSchema">

    <xs:import namespace="www.myns.com/XMLSchema" schemaLocation="xsd/notexistingfile.xsd"/>

    <xs:element name="mytype" type="n1:mytype">
    </xs:element>   

</xs:schema>

取消注释创建new SchemaFactory 的行可以解决该问题,但我希望SchemaFactory 应该用作单例。

第二次尝试不仅没有抛出SAXParseException,而且可以进行验证,如果是下面的sample.xml文件则成功:

<?xml version="1.0" encoding="UTF-8"?>

<mytype></mytype>

运行程序时的输出如下:

org.xml.sax.SAXParseException; systemId: file:/C:/workspace/XSD_PARSING/bin/com/myxsd/myxsd.xsd; lineNumber: 7; columnNumber: 45; src-resolve: Cannot resolve the name 'n1:mytype' to a(n) 'type definition' component.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseNamedElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseGlobal(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseSchemas(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(Unknown Source)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(Unknown Source)
at javax.xml.validation.SchemaFactory.newSchema(Unknown Source)
at javax.xml.validation.SchemaFactory.newSchema(Unknown Source)
at com.myxsd.ParseXSD.main(ParseXSD.java:22)

com.sun.org.apache.xerces.internal.jaxp.validation.SimpleXMLSchema 的实例包含以下功能列表:

"http://apache.org/xml/features/validation/schema/augment-psvi" true
"http://apache.org/xml/features/continue-after-fatal-error" false
"http://javax.xml.XMLConstants/feature/secure-processing" true
"http://apache.org/xml/features/honour-all-schemaLocations" false
"http://apache.org/xml/features/validate-annotations" false
"http://apache.org/xml/features/validation/schema-full-checking" true
"http://apache.org/xml/features/internal/tolerate-duplicates" false
"http://apache.org/xml/features/generate-synthetic-annotations" false
"http://apache.org/xml/features/namespace-growth" false
"http://www.oracle.com/feature/use-service-mechanism" false
"http://apache.org/xml/features/disallow-doctype-decl" false

【问题讨论】:

  • 您能检查一下第二次调用返回的架构类型吗?
  • com.sun.org.apache.xerces.internal.jaxp.validation.SimpleXMLSchema 的实例由后续调用返回。
  • 我在这两种情况下都遇到了错误。你在什么 JRE 上执行你的代码?您是否还在寻找一种忽略无效导入的方法,或者只是好奇为什么您没有收到异常?
  • 我尝试在 Java 6 和 Java 8 中构建和运行这个示例,两者都在 Windows 上。我很好奇原因,但它也可能是问题的根源,因为我的测试通过了,即使它们应该失败。
  • @stepasite 我在 SE 6、7 和 8 上进行了测试。在所有 3 上都得到了两个 catch 语句。你确定你正确地阅读了你的输出吗?

标签: java xml xsd xerces


【解决方案1】:

似乎一定是 SchemaFactory 正在缓存模式。因此,当您再次尝试读取它时,它会使用缓存的副本。

我对这个特定的 API 不是特别熟悉,更多地使用了 Xerces xni 接口,但我认为它们共享相同的底层实现,而且我知道 Xerces 可以并且在某些情况下会进行模式缓存。我很想知道为什么 SchemaFactory 甚至需要,除非它是为了做一些跨模式的簿记,比如缓存或允许你从部分构建模式。

我的建议是下载 JDK 源代码的副本并使用调试器进入 sf.newSchema 调用...我敢打赌您会看到缓存的架构。我不确定 JDK 使用什么实现类,但是如果您查看Xerces one,这里的 newSchema 方法(如果安装了此实现,抽象基类 SchemaFactory 方法最终将调用该方法)确实实例化了一个 GrammarPool。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    相关资源
    最近更新 更多