Java XML解析器添加了不必要的xmlns和XML:space属性



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

还要注意version="-//W3C//DTD XHTML 1.1//EN";那是我不需要也不想要的东西。

我做错什么了吗?有没有一种方法可以配置解析器不包含这个不必要的cruft

有趣的是,XHTML 1.0严格来说这不是问题。

<?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。(请参阅如何在JavaXMLDOM中强制从DTD中删除具有隐含默认值的属性。(但这是非常低效的;解析时最好完全关闭它。

我找到了一个变通方法,尽管它并不理想。其想法是,当文档要求使用XHTML 1.1 DTD-//W3C//DTD XHTML 1.1//EN进行解析时,实际使用XHTML 1.0严格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);

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

您尝试过nonvalidating/load-dtd-grammarXerces配置功能吗?

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

最新更新