我在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-grammar
Xerces配置功能吗?
但是,我一直在研究如何在Saxon中实现这一点,我并没有要求XML解析器不报告默认属性,而是在报告它们时丢弃它们。我使用Xerces作为SAX解析器,而不是DOM解析器。(在SAX中,使用Attributes2.isDefaulted()
报告默认属性(。