Java SaxParser XSD验证报错:无法解析global:Document类型定义
XSD Schema验证无法解析全局类型的解决办法
问题场景
我有一个引用多个XSD文件的根XSD,所有相关文件都已放入src/main/java/resources目录。使用SaxParser进行Schema验证XML文件时,出现如下错误:
org.xml.sax.SAXParseException; lineNumber: 27; columnNumber: 48; src-resolve: Cannot resolve the name 'global:Document' to a(n) 'type definition' component.
根XSD第27行代码:
<xsd:extension base="global:Document">
根XSD的schema命名空间声明:
<xsd:schema xmlns:global="urn:global:xsd:1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:global:company:xsd:2" elementFormDefault="unqualified" attributeFormDefault="unqualified" version="2.0">
根XSD中导入global.xsd的语句(第16行):
<xsd:import namespace="urn:global:xsd:1" schemaLocation="./global.xsd"/>
global.xsd确实存在于resources目录,IntelliJ可以通过cmd+鼠标点击跳转,但SaxParser无法识别该文件中的Document类型。global.xsd内容如下:
<xsd:schema xmlns:global="urn:global:xsd:1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:global:xsd:1" elementFormDefault="unqualified" attributeFormDefault="unqualified" version="1.0"> <xsd:annotation> <xsd:documentation> <global:copyright>Copyright (C) 2004 global Inc., All Rights Reserved.</global:copyright> <global:specification>global common components Version 1.0</global:specification> </xsd:documentation> </xsd:annotation> <xsd:complexType name="Document" abstract="true"> <xsd:annotation> <xsd:documentation xml:lang="en"> global document properties for all messages. </xsd:documentation> </xsd:annotation> <xsd:attribute name="schemaVersion" type="xsd:decimal" use="required"> <xsd:annotation> <xsd:documentation xml:lang="en"> The version of the schema corresponding to which the instance conforms. </xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="creationDate" type="xsd:dateTime" use="required"> <xsd:annotation> <xsd:documentation xml:lang="en"> The date the message was created. Used for auditing and logging. </xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:complexType> </xsd:schema>
由于依赖的XSD文件过多无法合并,求可行的解决办法。当前代码实现如下:
import java.io.InputStream; import javax.xml.XMLConstants; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; public class XmlSchemaValidator { private final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); private final Schema xmlCaptureSchema; private static final String[] XSD_FILES = { "/Types.xsd", "/Identification.xsd", "/Partner.xsd", "/Manifest.xsd", "/BusinessScope.xsd", "/BusinessDocumentHeader.xsd", "/global.xsd", "/global-1_1.xsd", "/global-query-1_1.xsd", "/global-masterdata-1_1.xsd", }; public XmlSchemaValidator() { //this.xmlCaptureSchema = loadSchema("/global-1_1.xsd"); this.xmlCaptureSchema = loadSchemaType2(); } public void validateAgainstCaptureSchema(final InputStream input) { try { final Validator validator = xmlCaptureSchema.newValidator(); validator.validate(new StreamSource(input)); } catch (Exception e) { e.printStackTrace(); } } public Schema loadSchema(final String name) { Schema schema = null; try { schema = schemaFactory.newSchema(new StreamSource(getClass().getResourceAsStream(name))); } catch (Exception e) { e.printStackTrace(); } return schema; } public Schema loadSchemaType2() { Schema schema = null; try { Source[] xsdSources = new Source[XSD_FILES.length]; int i = 0; for (String xsdFile : XSD_FILES) { final InputStream xsdStreamData = getClass().getResourceAsStream(xsdFile); final StreamSource xsdStreamSource = new StreamSource(xsdStreamData); xsdSources[i] = xsdStreamSource; i++; } schema = schemaFactory.newSchema(xsdSources); } catch (Exception e) { e.printStackTrace(); } return schema; } }
解决办法
问题核心是通过InputStream加载XSD时,StreamSource没有设置systemId,导致SaxParser无法识别XSD之间的相对路径引用。虽然你把所有XSD都加载到了Source数组中,但解析器处理根XSD的schemaLocation="./global.xsd"时,因为无法确定当前XSD的位置,无法解析相对路径指向的文件。
修改loadSchemaType2方法,为每个StreamSource设置对应的systemId即可解决:
public Schema loadSchemaType2() { Schema schema = null; try { Source[] xsdSources = new Source[XSD_FILES.length]; int i = 0; for (String xsdFile : XSD_FILES) { final InputStream xsdStreamData = getClass().getResourceAsStream(xsdFile); final StreamSource xsdStreamSource = new StreamSource(xsdStreamData); // 设置systemId为XSD文件的资源路径,让解析器能正确解析相对引用 xsdStreamSource.setSystemId(getClass().getResource(xsdFile).toExternalForm()); xsdSources[i] = xsdStreamSource; i++; } schema = schemaFactory.newSchema(xsdSources); } catch (Exception e) { e.printStackTrace(); } return schema; }
额外优化建议
- 根XSD中的
schemaLocation="./global.xsd"可以简化为global.xsd,避免相对路径的解析歧义,因为所有XSD都在同一目录下。 - 如果后续遇到更复杂的XSD引用场景,可以自定义
LSResourceResolver来统一管理XSD的加载逻辑,但设置systemId是当前场景下最直接有效的方案。
内容的提问来源于stack exchange,提问作者BATMAN_2008
相关产品推荐
相关产品推荐

