7

我正在根据模式验证 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"));

当我直接从类路径获取架构时,为什么验证不起作用?

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

失败时的异常消息: Could not validate against schema SampleResponse.xsd. Nested exception: src-resolve: Cannot resolve the name 'oa:Attachments' to a(n) 'element declaration' component.

4

3 回答 3

9

当您将 File 传递给 StreamSource 时,InputStream 被设置为文件的内容,而且 systemId 被设置为文件的 URL。这允许解析架构中的相对 URI。如果您的架构有任何相对 URL,这绝对是您的问题。要在从类路径读取架构时使这些相对 URL 可解析,您需要实现EntityResolver。如果您不使用相对 URI,则 systemId 为 null 可能还会产生其他更微妙的影响。我建议使用构造函数

StreamSource(InputStream inputStream, String systemId)

尝试将 systemId 设置为:null、包含架构的文件、其他文件、不存在的文件。这可能会给你一个关于 Validator 对 systemId 做了什么的提示。

于 2012-06-12T13:41:48.697 回答
2

我发现我不需要实现 EntityResolver 来使相对 URL 可以从类路径中解析。

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

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

设置验证源

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
Resource[] theResources = patternResolver.getResources("classpath:schemas/**/*.xsd");
List<Source> sources = new ArrayList<>();
for (Resource resource: theResources) {
    StreamSource dtd = new StreamSource(resource.getInputStream());
    dtd.setSystemId(resource.getURI().toString());
    sources.add(dtd);

patternResolver被赋予一个模式,该模式classpath:schemas/**/*.xsd允许它递归地在类路径上的schemas目录中查找所有 .xsd 文件。

.xsd 文件可以使用相对路径导入其他 .xsd 文件。例如,一个 .xsd 文件可以包含这样的导入:

<xsd:import namespace="urn:www.example.com/common" schemaLocation="./common.xsd">

这一行:

dtd.setSystemId(resource.getURI().toString());

是使架构验证器解析 .xsd 文件中的相对路径的关键。

执行验证

上面构建的StreamSource数组 ( sources ) 现在可用于设置 XML 验证的模式源:

import org.xmlunit.builder.Input;
import org.xmlunit.validation.Languages;
import org.xmlunit.validation.Validator;
import javax.xml.transform.Source;

Validator v = Validator.forLanguage(Languages.W3C_XML_SCHEMA_NS_URI);
v.setSchemaSources(sources.toArray(new Source[sources.size()]));
Source input = Input.fromByteArray(xmlBytes).build();
v.validateInstance(input);

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

于 2018-05-24T22:22:48.417 回答
0

对于后代,这是我在 Scala 中所做的事情,灵感来自 Joman68 的答案https://stackoverflow.com/a/50518995/434405,它不使用弹簧库。

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)
}
于 2020-10-14T13:01:25.803 回答