【问题标题】:Java XML parser adding unnecessary xmlns and xml:space attributesJava XML 解析器添加了不必要的 xmlns 和 xml:space 属性
【发布时间】:2020-04-03 00:26:34
【问题描述】:

我在 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" 似乎完全不正确;我想只有像&lt;pre&gt; 这样的元素应该设置xml:space="preserve"。 (更新:HTML5 specification 表示默认情况下 HTML 会保留空间,并且 xml:space 不能在 HTML 中序列化,所以这可能是这里的部​​分原因。我将改进我的 HTML 序列化器忽略xml:space 属性,这将部分缓解此问题。)

还要注意version="-//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。 (请参阅How to force removal of attributes with implied default values from DTD in Java XML DOM。)但这非常低效;解析时最好完全关闭它。

【问题讨论】:

  • 什么是DefaultEntityResolver?另外,关于“由于某种原因它添加了额外的属性”,你的意思是它被添加到原始的 xml 文件中吗?如果没有,如何将Document 对象转换为 XML 字符串(或将其保存到文件中)?
  • DefaultEntityResolver 是一个实体解析器,它在本地提供实体(例如 DTD),而不是从 Internet 下载它们。如果要验证实体的内容,请参阅票证 globalmentor.atlassian.net/browse/JAVA-175 或克隆提交 bitbucket.org/globalmentor/…
  • 当我说“出于某种原因它添加了额外的属性”时,我的意思是它正在将它们添加到 DOM 树中。我正在使用我自己编写的自定义 XML 序列化程序来打印树。
  • 如果删除 DefaultEntityResolver 会发生什么?
  • 如果我删除了DefaultEntityResolver,那么它甚至不会解析,并且因为java.io.FileNotFoundException for http://www.w3.org/TR/xhtml11/DTD/xhtml-datatypes-1.mod 而崩溃。显然,W3C 已经从其网站上完全删除了这个实体。 (那么现在没有人使用标准的 Java XML 解析器解析 XHTML 1.1 文件了吗?这完全被破坏了。)所以我必须使用自定义实体解析器来处理本地存储的实体,否则它甚至不会解析。 @Olivier 你自己试过了吗?

标签: java xml xml-parsing xml-namespaces dtd


【解决方案1】:

我找到了一种解决方法,尽管它并不理想。这个想法是,当要求使用 XHTML 1.1 DTD -//W3C//DTD XHTML 1.1//EN 解析文档时,真正使用 XHTML 1.0 Strict DTD -//W3C//DTD XHTML 1.0 Strict//EN 代替。对于大多数实际用途,此 DTD 是他们要求的 effectively almost the same,但它并没有引入所有默认内容。

记住 DefaultEntityResolver 是我的实体解析器,其中预定义了大多数 XHTML DTD(请参阅 Complete list of XHTML, MathML, and SVG modules and other entities, with public identifiers?),实现看起来像这样:

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);

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

【讨论】:

    【解决方案2】:

    您是否尝试过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 解析器,但除此之外我没有跟上最新的发展。
    • Apache Xerces 支持的特性似乎比 JDK 版本支持的特性有更好的文档记录。我仍然出于习惯使用 Apache Xerces,因为多年来它比 JDK 版本可靠得多,但这些天来这种理由似乎已经消失了。
    • 今天有趣的是我发现HTML5xml:space 甚至不应该在HTML 中序列化,所以我将更新我的HTML 序列化程序以忽略xml:space;这将部分缓解这种情况。同时,在我更新我的 HTML 序列化程序以删除多余的命名空间声明并弄清楚如何优雅地摆脱 XHTML version 属性之前,我仍然会使用我的解决方法。
    猜你喜欢
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 2012-10-24
    • 2012-09-27
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多