You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SAX结合RelaxNG校验XML报no grammar found错误如何解决

问题

我正尝试使用SAX解析XML文件,并基于RelaxNG模式对文件做合法性校验,当前实现代码如下:

System.setProperty(SchemaFactory.class.getName() + ":" + XMLConstants.RELAXNG_NS_URI, "com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory"); 
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);
Schema schema = sf.newSchema(scf);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setSchema(schema);

try {
    SAXParser parser = factory.newSAXParser ();
    XMLReader reader = parser.getXMLReader ();
    reader.setFeature("http://xml.org/sax/features/validation", true);
    reader.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
                        schemaFilename);
    reader.setFeature("http://apache.org/xml/features/xinclude", true);
    reader.setFeature("http://xml.org/sax/features/namespaces", true);
    reader.setFeature("http://apache.org/xml/features/xinclude/fixup-base-uris", false);
    reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    
    reader.setErrorHandler   (this);
    reader.setContentHandler (this);
    reader.setEntityResolver (this);

    reader.parse (new InputSource(input));
}

运行上述代码时,程序抛出如下错误:

Document is invalid: no grammar found.

经测试确认以下两点:

  • 待校验的XML文档与对应RelaxNG模式使用jing工具校验均可正常通过
  • 若使用trang将RelaxNG模式转换为XSD格式后,替换原有RNG模式执行校验,整个流程可正常运行

初步排查:怀疑实例化SAXParserFactory对象时,需要显式指定使用RelaxNG对应的解析器工厂。RelaxNG官方文档提及RelaxNG“可作为类库配合任意SAX2解析器完成校验”,但对应文档链接已失效;查阅jing-trang的RelaxNG源码仓库,也未找到名称匹配.*ParseFactory.*规则的类。

求问:该如何调整配置,才能基于SAX正常使用RelaxNG完成XML校验?


解决方案

报错是配置冲突导致的,你混用了W3C XSD专属的校验配置和JAXP标准的RelaxNG校验流程,根本不需要找什么RelaxNG专属的SAXParserFactory实现,改两处配置就能正常跑:

  • 删掉XSD专属的模式地址配置
    你代码里设置的http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation是Apache Xerces专门给无命名空间W3C XSD用的外部模式加载属性,RelaxNG校验完全不认这个配置。你之前已经通过SchemaFactory加载了RNG模式,又通过factory.setSchema(schema)把校验规则绑到了解析器工厂,不需要再重复指定模式路径。留着这个属性反而会让解析器主动去找XSD语法规则,找不到就直接抛no grammar found的错误。
  • 关掉SAX默认的validation开关
    把reader.setFeature("http://xml.org/sax/features/validation", true)这行删掉,或者设为false。这个特性开了之后会触发解析器内置的DTD/XSD默认校验逻辑,和你通过setSchema挂载的RelaxNG校验器是两套独立流程,开着会干扰RNG校验的正常执行。

修正后的核心可用代码:

// 注册RNG的SchemaFactory实现,这部分逻辑是对的,不用改
System.setProperty(SchemaFactory.class.getName() + ":" + XMLConstants.RELAXNG_NS_URI, "com.thaiopensource.relaxng.jaxp.XMLSyntaxSchemaFactory"); 
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);
// 确保scf是指向正确RNG模式文件的源即可
Schema schema = sf.newSchema(scf);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setSchema(schema);
// 注意:命名空间感知必须在工厂层面开启,RNG校验依赖命名空间匹配
factory.setNamespaceAware(true);

SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
// 移除原有的validation特性设置、移除external-noNamespaceSchemaLocation属性设置
reader.setFeature("http://apache.org/xml/features/xinclude", true);
reader.setFeature("http://apache.org/xml/features/xinclude/fixup-base-uris", false);
reader.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

reader.setErrorHandler(this);
reader.setContentHandler(this);
reader.setEntityResolver(this);
reader.parse(new InputSource(input));

补个容易踩的坑:setNamespaceAware(true)一定要在SAXParserFactory实例上设置,别等拿到XMLReader对象再去改命名空间特性。工厂初始化解析器的时候如果没开命名空间感知,后续再改reader配置会导致RNG校验的命名空间匹配逻辑失效。

JAXP的校验机制本身是跨模式语言统一的:只要正确注册了对应模式语言的SchemaFactory,通过setSchema绑定到解析器工厂后,不管底层用的是Xerces还是其他兼容SAX2的解析器,都会自动挂载对应的校验器完成校验,这也是文档里说的RelaxNG可以配合任意SAX2解析器工作的实际含义,不需要找专门的解析器工厂类。


内容的提问来源于stack exchange,提问作者opeongo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 18:15:44