我正在根据模式验证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.
答案 0 :(得分:4)
将File传递给StreamSource时,InputStream设置为文件的内容,而systemId也设置为File的URL。这允许解析模式中的相对URI。如果您的架构有任何相对URL,这绝对是您的问题。要在从类路径中读取架构时使这些相对URL可解析,您需要实现EntityResolver。如果您不使用相对URI,则systemId可能仍然存在其他更微妙的影响。我建议使用构造函数
StreamSource(InputStream inputStream, String systemId)
尝试将systemId设置为:null,包含架构的File,其他一些文件,不存在的File。这可能会给你一些关于Validator在systemId上做什么的提示。
答案 1 :(得分:0)
我发现我不需要实现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。
答案 2 :(得分:0)
为了后代,这是我在Scala中执行的,受Joman68的答案https://stackoverflow.com/a/50518995/434405的启发,该操作没有使用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)
}