
在使用jaxp(如xerces)解析xml时,若`xsi:nonamespaceschemalocation`等属性值为相对路径(如`xsd/book.xsd`),解析器会默认以`user.dir`为基准解析为绝对文件路径(如`file:///home/user/xsd/book.xsd`),导致定位失败;根本解决方法是显式提供xml文档的基准uri(base uri),而非依赖默认回退行为。
该问题本质源于Xerces(及多数JAXP实现)对未明确指定base URI的输入源所采取的降级策略:当XML文档本身未携带有效系统标识符(system ID)时,解析器会将当前工作目录(即System.getProperty("user.dir"))作为解析相对URI的基准——这并非标准强制要求,而是实现层面的“善意假设”,却常引发路径错位、Schema加载失败等问题。
✅ 正确做法:确保输入源携带有效的base URI
不要直接使用无标识的FileInputStream:
// ❌ 错误:无systemId → 触发user.dir fallback
FileInputStream fis = new FileInputStream("src/main/resources/book.xml");
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(fis); // systemId = null → base = file:///.../user.dir✅ 而应通过InputSource显式设置systemId(即文档的逻辑/物理位置):
立即学习“Java免费学习笔记(深入)”;
// ✅ 正确:显式声明base URI,相对路径将据此解析
File xmlFile = new File("src/main/resources/book.xml");
InputSource source = new InputSource(new FileInputStream(xmlFile));
source.setSystemId(xmlFile.toURI().toString()); // e.g., "file:///.../book.xml"
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(source); // now "xsd/book.xsd" resolves relative to book.xml's dir对于SAXParser或SchemaFactory等场景,同样适用:
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// 传入带有base URI的StreamSource
StreamSource schemaSource = new StreamSource(new File("xsd/book.xsd").toURI().toString());
Schema schema = sf.newSchema(schemaSource);⚠️ 注意事项:
- setSystemId() 中的URI必须是合法、可解析的URI(推荐用File.toURI()而非File.getAbsolutePath());
- 若XML通过网络加载(如HTTP),systemId应设为对应URL,此时相对schema路径将按RFC 3986规则解析;
- 不要依赖EntityResolver2反向从绝对路径提取原始相对名——这属于脆弱的hack,且无法覆盖所有解析阶段(如Schema预加载);
- 在Spring等框架中集成时,确保Resource或InputStreamSource已正确封装URI上下文(例如使用ClassPathResource并调用getURI())。
总结:相对systemId的行为本身并无问题,问题在于缺失明确的解析上下文。通过主动赋予XML输入源准确的systemId,即可让Xerces严格按XML Base规范(RFC 3986)解析相对引用,彻底规避user.dir干扰,实现健壮、可移植的XML Schema验证。










