Java 的 XML 解析器在添加无用的 xmlns 和 xml:space 属性。

4

我正在Windows 10上使用Java 11(AdoptOpenJDK 11.0.5 2019-10-15)。我正在解析一些遗留的XHTML 1.1文件,它们通常具有以下形式:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" http://www.w3.org/MarkUp/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <title>XHTML 1.1 Skeleton</title>
</head>
<body>
</body>
</html>

我正在使用一个简单的非验证解析器:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
final Document document;
try (InputStream inputStream = new BufferedInputStream(getClass().getResourceAsStream("xhtml-1.1-test.xhtml"))) {
  document = documentBuilder.parse(inputStream);
}

由于某些原因,它会在各个地方添加额外的属性,例如xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"xml:space = "preserve"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" version="-//W3C//DTD XHTML 1.1//EN" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:lang="en">
<head xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <title xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">XHTML 1.1 Skeleton</title>
</head>
<body xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xml:space="preserve"></body>
</html>

我知道DTD可以提供默认的属性值,但我不明白为什么要添加xmlns:xsi属性,因为似乎没有元素或属性在该命名空间中。
此外,xml:space="preserve"似乎完全不正确;只有像
这样的元素应该设置xml:space="preserve",我认为。(更新:HTML5规范指出,默认情况下HTML保留空格,而且在HTML中不能序列化xml:space属性,所以可能这是这里的部分原因。我将改进我的HTML序列化程序,忽略xml:space属性,这将部分缓解这个问题。)
还要注意版本为"-//W3C//DTD XHTML 1.1//EN";这是我不需要也不想要的。
我是否做错了什么?有没有办法配置解析器不包括这些不必要的东西?
有趣的是,这在XHTML 1.0 strict中不是问题。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>XHTML 1.0 Skeleton</title>
</head>
<body>
</body>
</html>

当解析时,会得到预期的结果:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>XHTML 1.0 Skeleton</title>
</head>
<body>
</body>
</html>

但这是一个与“-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN”有关的问题。因此,这似乎只是XHTML 1.1的问题。
更新:我有一些潜在有用的消息:如果我创建一个没有DTD的新文档,并将整个文档树导入到新文档中,所有这些不必要的内容(显然来自DTD中的隐含属性)都会消失,因为目标文档根本没有DTD。(请参见如何强制从Java XML DOM中的DTD中删除具有暗示默认值的属性)。但这非常低效;在解析时完全关闭这个功能会很好。

什么是DefaultEntityResolver?此外,关于“由于某种原因它添加了额外的属性”,你是否指它被添加到了原始的xml文件中?如果不是,那么你如何将Document对象转换为XML字符串(或保存到文件中)? - Eng.Fouad
DefaultEntityResolver 是一个实体解析器,可以本地提供实体(例如 DTD),而不是从互联网下载。如果您想要验证实体内容,请参见票 https://globalmentor.atlassian.net/browse/JAVA-175 或克隆提交 https://bitbucket.org/globalmentor/%7B15a9ee2f-e9fa-4dd1-8185-9ef4e2f6c713%7D/commits/4f74e003fa6c91600ab11fc379ea1712b86ab415。 - Garret Wilson
当我说“由于某种原因它正在添加额外的属性”时,我的意思是它正在将它们添加到DOM树中。我正在使用我自己编写的自定义XML序列化程序来打印出这棵树。 - Garret Wilson
如果您删除DefaultEntityResolver会发生什么? - Olivier
1
如果我删除DefaultEntityResolver,那么它甚至无法解析,并且由于http://www.w3.org/TR/xhtml11/DTD/xhtml-datatypes-1.modjava.io.FileNotFoundException而崩溃。显然,W3C已经从其网站中完全删除了此实体。(那么没有人再使用标准Java XML解析器解析XHTML 1.1文件吗?这是完全错误的。)因此,我必须使用具有本地存储实体的自定义实体解析器,否则它甚至无法解析。@Olivier你自己试过吗? - Garret Wilson
1
请注意,如果我在DTD系统ID中使用http://www.w3.org/MarkUp/DTD/xhtml11.dtd,即使不使用DefaultEntityResolver,它也可以成功解析(尽管在我的系统上访问DTD大约需要10分钟)。不幸的是,结果相同。我将更新问题以使用此工作URL,并删除DefaultEntityResolver。我已经创建了https://stackoverflow.com/q/60655704/421049,关于另一个URL中缺少的实体。 - Garret Wilson
2个回答

0

我找到了一个解决方法,虽然不是理想的。这个想法是当一个文档要求使用XHTML 1.1 DTD -//W3C//DTD XHTML 1.1//EN进行解析时,实际上使用XHTML 1.0 Strict DTD -//W3C//DTD XHTML 1.0 Strict//EN代替。对于大多数实际目的来说,这个DTD与他们要求的几乎相同,但它不会带来所有默认的垃圾。

记住DefaultEntityResolver是我的实体解析器,其中大多数XHTML DTD都是预定义的(请参见XHTML、MathML和SVG模块以及其他实体的完整列表,包括公共标识符?),实现看起来像这样:

private static final EntityResolver XHTML_1_1_TO_XHTML_1_0_ENTITY_RESOLVER =
    new EntityResolver() {

  private final EntityResolver defaultEntityResolver = DefaultEntityResolver.getInstance();

  @Override
  public InputSource resolveEntity(final String publicID, final String systemID)
      throws SAXException, IOException {
    if(XHTML_1_1_PUBLIC_ID.equals(publicID)) {
      final InputSource inputSource = resolveEntity(XHTML_1_0_STRICT_PUBLIC_ID, systemID);
      inputSource.setPublicId(publicID);
      return inputSource;
    }
    return defaultEntityResolver.resolveEntity(publicID, systemID);
  }

};

然后在解析时我会使用该实体解析器:

documentBuilder.setEntityResolver(XHTML_1_1_TO_XHTML_1_0_ENTITY_RESOLVER);

这有点笨拙,语义上我不太喜欢。但对于我的应用程序,我只需要一个干净、格式良好的解析文档,正确地替换实体,因此在实践中,它可能会为大多数文档产生有效的相同结果。


0

你尝试过使用nonvalidating/load-dtd-grammar Xerces配置功能吗?

然而,我刚刚在研究如何在Saxon中实现这一点,我并没有要求XML解析器不报告默认属性,而是在报告时将其丢弃。我正在使用Xerces作为SAX解析器而不是DOM解析器。(在SAX中,默认属性使用Attributes2.isDefaulted()进行报告)。


你尝试过nonvalidating/load-dtd-grammar Xerces配置特性吗?不,我从未听说过。它是否保证被内置的Java 11 XML解析器支持?坦率地说,我在过去几年中没有关注Java XML解析的状态。1999年我编写了一个完整的XML解析器,因为现有的Java解析器太慢了。在那时左右,Java将W3C DOM API集成到标准库中。当那时发生时,我从我的解析器转换到Java解析器,但除此之外,我没有关注最新的发展。 - Garret Wilson
Apache Xerces所支持的功能似乎比JDK版本支持的功能有更好的文档记录。我仍然出于习惯使用Apache Xerces,因为多年来它比JDK版本更可靠,但这种理由似乎在今天已经消失了。 - Michael Kay
有趣的是,今天我发现HTML5xml:space甚至不应该在HTML中序列化,所以我将更新我的HTML序列化程序以忽略xml:space;这将部分缓解这个问题。同时,在我更新我的HTML序列化程序以删除冗余的命名空间声明并找出如何优雅地摆脱XHTML version属性之前,我仍将使用我的解决方法。 - Garret Wilson

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