当模式来自类路径时,Java XML验证不起作用

当模式来自类路径时,Java XML验证不起作用,java,xml,xsd,xml-validation,Java,Xml,Xsd,Xml Validation,我正在根据模式验证XML文档。某些更复杂的文档/架构在尝试使用以下代码验证时总是失败: DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); dbfac.setNamespaceAware(true); dbfac.setIgnoringElementContentWhitespace(true); DocumentBuilder docBuilder = dbfac.newDoc

我正在根据模式验证XML文档。某些更复杂的文档/架构在尝试使用以下代码验证时总是失败:

    DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
    dbfac.setNamespaceAware(true);
    dbfac.setIgnoringElementContentWhitespace(true);
    DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
    Document doc = docBuilder.parse("sampleResponse.xml");

    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Source schemaSource = new StreamSource(getClass().getResourceAsStream("/" + "SampleResponse.xsd"));

    Schema schema = schemaFactory.newSchema(schemaSource);
    Validator validator = schema.newValidator();
    Source source = new DOMSource(doc);
    // Set a custom error handler that simple re-throws every exception
    validator.setErrorHandler(new ValidationErrorHandler());
    validator.validate(source);
问题在于这一行:

    Source schemaSource = new StreamSource(getClass().getResourceAsStream("/" + "SampleResponse.xsd"));
如果我将架构作为文件读取,它将工作:

    Source schemaSource = new StreamSource(new File("somepath/SampleResponse.xsd"));
当我直接从类路径获取模式时,为什么验证不起作用

(在64位Windows 7上使用Java 1.6)

失败时的异常消息:
无法针对架构SamplerResponse.xsd进行验证。嵌套异常:src resolve:无法将名称“oa:Attachments”解析为(n)“元素声明”组件。

将文件传递给StreamSource时,InputStream被设置为文件的内容,但systemId也被设置为文件的URL。这允许解析模式中的相对URI。如果您的模式有任何相对URL,这肯定是您的问题。要在从类路径读取模式时解析这些相对URL,需要实现一个。如果不使用相对URI,systemId为null可能还有其他更微妙的影响。我建议使用构造函数

StreamSource(InputStream inputStream, String systemId)

尝试将systemId设置为:null,包含架构的文件,其他文件,一个不存在的文件。这可能会给你一个提示,告诉你验证器是如何处理systemId的。

我发现我不需要实现EntityResolver,就可以从类路径解析相对URL

将系统id设置为类路径资源的URI就足够了

下面是一个使用Spring从类路径上的.xsd文件构建StreamSources列表的工作示例

设置验证源
import org.springframework.core.io.Resource;
导入org.springframework.core.io.support.PathMatchingResourcePatternResolver;
导入javax.xml.transform.Source;
导入javax.xml.transform.stream.StreamSource;
PathMatchingResourcePatternResolver patternResolver=新的PathMatchingResourcePatternResolver();
Resource[]theResources=patternResolver.getResources(“类路径:schemas/***.xsd”);
列表源=新的ArrayList();
for(资源:theResources){
StreamSource dtd=新的StreamSource(resource.getInputStream());
setSystemId(resource.getURI().toString());
来源。添加(dtd);
patternResolver有一个模式
classpath:schemas/***.xsd
,它允许递归地查找类路径上schemas目录中的所有.xsd文件

.xsd文件可以使用相对路径导入其他.xsd文件。例如,.xsd文件可以包含如下导入:

<xsd:import namespace="urn:www.example.com/common" schemaLocation="./common.xsd">
是模式验证器解析.xsd文件中的相对路径的关键

执行验证 上面构建的StreamSource数组(源)现在可用于设置XML验证的架构源:

import org.xmlunit.builder.Input;
导入org.xmlunit.validation.Languages;
导入org.xmlunit.validation.Validator;
导入javax.xml.transform.Source;
Validator v=Validator.forLanguage(Languages.W3C\u XML\u SCHEMA\u NS\u URI);
v、 setSchemaSources(sources.toArray(新源[sources.size()]);
Source input=input.fromByteArray(xmlBytes.build();
v、 验证实例(输入);

validateInstance方法调用验证xmlBytes数组表示的XML。

对于后代,我在Scala中做了一些事情,灵感来自Joman68的答案,它不使用spring libs

import javax.xml.XMLConstants
import javax.xml.transform.Source
import javax.xml.transform.stream.StreamSource
import javax.xml.validation.{Schema, SchemaFactory, Validator}

object SchemaCheck extends App {

  private val xsds = List("schema.xsd") // add more as required

  private val schemaDocuments: Array[Source] = xsds.map { xsd =>
    val res = getClass.getResource(s"/$xsd")
    val dtd = new StreamSource(res.toURI.toString)
    dtd.setSystemId(res.toURI.toString)
    dtd
  }.toArray

  private val sf           = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
  private val s: Schema    = sf.newSchema(schemaDocuments)
  private val v: Validator = s.newValidator()

  private val instanceDocument: Source = new StreamSource(new java.io.File("test.xml"))
  v.validate(instanceDocument)
}

是的,下面的代码现在可以使用:
Source schemaSource=newstreamSource(getClass().getResourceAsStream(“/”+“SampleResponse.xsd”)、getClass().getResource(“/”+“SampleResponse.xsd”).toString()
它能在所有系统上工作吗?我希望它能工作,但我从来没有测试过它。我的猜测是它必须是非空的。我会尝试将它设置为任意值,只是为了证明实际值并不重要。我会对它进行注释以明确这一点。但我认为你有正确的想法,使用你用来从类加载器获取它的URI。救生建议。设置systemId像魔术一样解决了我的问题。在我与EntityResolver搏斗之前,没有任何运气。