如何在Java中读取格式良好的XML,但跳过模式?

15
我想读取一个包含模式声明的XML文件。我只想读取它,不在意它是否有效,但我希望它是格式良好的。问题在于读取器正在尝试读取模式文件并失败了。我不希望它甚至尝试这样做。 我已经尝试禁用验证,但它仍然坚持尝试读取模式文件。理想情况下,我希望能够使用标准的Java 5 JDK完成这项任务。以下是我目前拥有的非常简单的代码:
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setValidating(false);
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(file);

这里是我得到的异常信息:

java.lang.RuntimeException: java.io.IOException: Server returned HTTP response code: 503 for URL: http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd

是的,这碰巧是一个XHTML模式,但这不是一个"XHTML"问题,而是一个XML问题。我只是指出这一点,以免人们分心。在这种情况下,W3C基本上是在说"不要请求这个东西,这是一个愚蠢的想法",我也同意。但是,这仍然只是问题的细节,不是根本原因。我根本不想请求它。


这似乎与以下有关:https://dev59.com/AXVC5IYBdhLWcg3w4Vb6 - jt.
5个回答

17

这个参考文献不是针对 Schema,而是针对一个DTD

DTD文件不仅包含结构规则,还可以包含实体引用。XML解析器必须加载和解析DTD引用,因为它们可能包含实体引用,这可能会影响文档的解析方式和文件内容(你可能有字符或整个文本短语的实体引用)。

如果您想避免加载和解析引用的DTD,您可以提供自己的EntityResolver并检测引用的DTD,决定是加载DTD文件的本地副本还是只返回null。

来自相关答案中的自定义EntityResolvers示例代码:

   builder.setEntityResolver(new EntityResolver() {
        @Override
        public InputSource resolveEntity(String publicId, String systemId)
                throws SAXException, IOException {
            if (systemId.contains("foo.dtd")) {
                return new InputSource(new StringReader(""));
            } else {
                return null;
            }
        }
    });

我曾经想过这就是我必须要做的事情,我只是创建了一个“空”的EntityResolver,它始终为所有内容返回空InputSource。这似乎起到了作用。 - Will Hartung
我遇到了同样的问题,我采用了这个解决方案。它解决了IOException。我的担忧是,使用空实体解析器会丢失DOCTYPE。我希望保留这个DOCTYPE,并不从输入中删除它。这可能吗? - Rachel

10

最简单的答案是在创建DocumentBuilderFactory之后调用这个一行代码:

dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

不加掩饰地借用自Make DocumentBuilder.parse ignore DTD references.


这很好,但是如果您要解析的文档确实包含DTD怎么办?使用此标志,您将只会得到一个SAXParseException并且整个过程将失败。简单地忽略DTD,但继续解析更加灵活。 - Dmitry Pashkevich

1
这里的问题不在于验证。无论验证设置如何,解析器仍会尝试解析文档中的任何引用,例如实体、DTD和(有时)模式。只有在稍后它才决定使用它们进行验证(或不验证)。您需要插入一个实体解析器来“拦截”这些取消引用的尝试。
请查看Apache XML Resolver,以了解一种相对容易的方法来完成此操作。

0

我没有测试过这个,但你可以尝试在工厂上调用setSchema并传递null。

例如:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
dbf.setSchema(null);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);

更新:查看DocumentBuilderImpl,看起来这可能有效,从构造函数开始,它将在检查模式之前从工厂检查语法。
从DocumentBuilderFactoryImpl:
public void setSchema(Schema grammar) {
    this.grammar = grammar;
}

从DocumentBuilderImpl构造函数:

...
this.grammar = dbf.getSchema();
if (grammar != null) {
    XMLParserConfiguration config = domParser.getXMLParserConfiguration();
    XMLComponent validatorComponent = null;
    /** For Xerces grammars, use built-in schema validator. **/
    ...
}

-1

这个方法可以很好地检查XML是否格式正确,无论它是否包含DTD声明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接