Java XML DOM:id Attributes如何特殊?

2022-09-02 02:15:06

该类的 javadoc 在 下有以下注释。DocumentgetElementById

注: 名称为“ID”或“id”的属性不是 ID 类型,除非如此定义

所以,我在DOM中读取了一个XHTML文档(使用Xerces 2.9.1)。

该文档中有一个普通的旧文档。<p id='fribble'>

我调用 ,它返回 null。getElementById("fribble")

我使用XPath来获取“//*[id='fribble']”,一切都很好。

所以,问题是,是什么原因导致将ID属性实际标记为“如此定义”?DocumentBuilder


答案 1

这些属性之所以特殊,是因为它们的类型,而不是因为它们的名称

XML 中的 ID

虽然很容易将属性视为值是一个简单的字符串,但这并不是完整的故事 - 还有一个与属性关联的属性类型name="value"

当涉及 XML 架构时,这很容易理解,因为 XML 架构支持 XML 元素和 XML 属性的数据类型。XML 属性被定义为简单类型(例如 xs:string、xs:integer、xs:dateTime、xs:anyURI)。此处讨论的属性是使用内置数据类型定义的(请参阅 XML 架构第 2 部分:数据类型的第 3.3.8 节)。xs:ID

<xs:element name="foo">
  <xs:complexType>
   ...
   <xs:attribute name="bar" type="xs:ID"/>
   ...
  </xs:complexType>
</xs:element>

尽管 DTD 不支持 XML 架构中的丰富数据类型,但它确实支持一组有限的属性类型(在 XML 1.0 的第 3.3.1 节中定义)。此处讨论的属性是使用 属性类型 定义的。ID

<!ATTLIST foo  bar ID #IMPLIED>

使用上述 XML 架构或 DTD,以下元素将由 ID 值“xyz”标识。

<foo bar="xyz"/>

如果不知道 XML 架构或 DTD,就无法分辨什么是 ID,什么不是:

  • 名称为“id”的属性不一定具有 ID 的属性类型;和
  • 名称不是“id”的属性可能具有 ID 的属性类型

为了改善这种情况,随后发明了 (请参阅 xml:id W3C 建议)。这是一个始终具有相同的前缀和名称的属性,旨在被视为属性类型为 ID 的属性。但是,它是否这样做将取决于所使用的解析器是否知道。由于许多解析器最初是在定义之前编写的,因此可能不受支持。xml:idxml:idxml:id

爪哇中的 ID

在 Java 中,通过查找 ID 类型的属性来查找元素,而不是通过查找名称为“id”的属性来查找元素。getElementById()

在上面的示例中,将返回该元素,即使其上的属性名称不是“id”(假设 DOM 知道它具有 ID 的属性类型)。getElementById("xyz")foobar

那么 DOM 如何知道属性具有什么属性类型呢?有三种方法:

  1. 向分析器提供 XML 架构(示例)
  2. 向解析器提供 DTD
  3. 向 DOM 显式指示它被视为 ID 的属性类型。

第三个选项是使用 org.w3c.dom.Element上的 or 或 方法完成的。setIdAttribute()setIdAttributeNS()setIdAttributeNode()

Document doc;
Element fooElem;

doc = ...; // load XML document instance
fooElem = ...; // locate the element node "foo" in doc

fooElem.setIdAttribute("bar", true); // without this, 'found' would be null

Element found = doc.getElementById("xyz");

必须为具有这些类型的属性之一的每个元素节点执行此操作。没有简单的内置方法可以使所有具有给定名称(例如“id”)的属性都属于属性类型ID。

第三种方法仅在调用 的代码与创建 DOM 的代码分开的情况下有用。如果它是相同的代码,则它已经找到了用于设置 ID 属性的元素,因此不太可能需要调用 。getElementById()getElementById()

另外,请注意,这些方法不在原始 DOM 规范中。是在 DOM 级别 2 中引入的。getElementById

XPath 中的 ID

原始问题中的 XPath 给出了一个结果,因为它只与属性名称匹配。

要匹配属性类型 ID 值,需要使用 XPath 函数(它是 XPath 1.0 中的节点集函数之一):id

id("xyz")

如果使用了该方法,XPath 将给出与(即未找到匹配项)相同的结果。getElementById()

XML 中的 ID(续)

应突出显示 ID 的两个重要功能。

首先,属性类型 ID 的所有属性的值对于整个 XML 文档必须是唯一的。在以下示例中,如果并且两者都具有 ID 的属性类型,则添加另一个 id24601 的公司将是错误的,因为它将是现有 ID 值的副本。即使属性名称不同,重要的是属性类型personIdcompanyIdcompanyId

<test1>
 <person personId="id24600">...</person>
 <person personId="id24601">...</person>
 <company companyId="id12345">...</company>
 <company companyId="id12346">...</company>
</test1>

其次,属性是在元素上定义的,而不是在整个 XML 文档上定义的。因此,在不同元素上具有相同属性名称的属性可能具有不同的属性类型属性。在下面的示例 XML 文档中,如果只有属性类型为 ID(并且没有其他属性),将返回一个元素,但不会返回(因为 不是属性类型 ID)。此外,属性具有与 相同的值也不是错误,该值在 XML 文档中 ID 的唯一性中不被考虑,因为它不是属性类型 ID。alpha/@bargetElementById("xyz")getElementById("abc")beta/@bargamma/@baralpha/@bar

<test2>
  <alpha bar="xyz"/>
  <beta bar="abc"/>
  <gamma bar="xyz"/>
</test2>

答案 2

要使调用正常工作,必须知道其节点的类型,并且目标节点必须是 XML ID 类型,方法才能找到它。它通过关联的架构了解其元素的类型。如果未设置架构,或者未将属性声明为 XML ID 类型,则永远不会找到它。getElementById()DocumentidgetElementById()

我的猜测是,您的文档不知道该元素的属性是 XML ID 类型(是吗?)。您可以使用和其他 DOM 遍历函数导航到 DOM 中的节点,并尝试调用 id 属性来确定。pidgetChildNodes()Attr.isId()

From the getElementById javadoc:

DOM 实现应使用属性 Attr.isId 来确定属性是否为 ID 类型。

注: 名称为“ID”或“id”的属性不是 ID 类型,除非有此定义。

如果您使用 将 XML 解析为 DOM,请确保在调用 newDocumentBuilder() 之前在 DocumentBuilderFactory 上调用 setSchema(schema),以确保您从工厂获得的构建器知道元素类型。DocumentBuilder