Java XML DOM:id属性如何特殊?

bma*_*ies 19 java dom

Document该类的javadoc 下面有以下注释getElementById.

注意:除非如此定义,否则名称为"ID"或"id"的属性不是ID类型

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

该文件有一个简单的旧<p id='fribble'>.

我打电话getElementById("fribble"),它返回null.

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

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

Hoy*_*len 50

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

XML中的ID

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

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

<xs:element name="foo">
  <xs:complexType>
   ...
   <xs:attribute name="bar" type="xs:ID"/>
   ...
  </xs:complexType>
</xs:element>
Run Code Online (Sandbox Code Playgroud)

虽然DTD不支持XML Schema中的rich数据类型,但它支持一组有限的属性类型(在XML 1.0的3.3.1节中定义).这里讨论的属性是使用属性类型定义的ID.

<!ATTLIST foo  bar ID #IMPLIED>
Run Code Online (Sandbox Code Playgroud)

使用上述XML Schema或DTD,以下元素将由ID值"xyz"标识.

<foo bar="xyz"/>
Run Code Online (Sandbox Code Playgroud)

在不知道XML Schema或DTD的情况下,无法分辨什么是ID,什么不是:

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

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

Java中的ID

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

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

那么DOM如何知道属性具有哪种属性类型?有三种方式:

  1. 为解析器提供XML Schema(示例)
  2. 为解析器提供DTD
  3. 向DOM明确指出它被视为ID的属性类型.

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

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");
Run Code Online (Sandbox Code Playgroud)

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

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

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

XPath中的ID

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

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

id("xyz")
Run Code Online (Sandbox Code Playgroud)

如果已经使用过,那么XPath会给出相同的结果getElementById()(即找不到匹配项).

XML中的ID继续

应强调ID的两个重要特征.

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

<test1>
 <person personId="id24600">...</person>
 <person personId="id24601">...</person>
 <company companyId="id12345">...</company>
 <company companyId="id12346">...</company>
</test1>
Run Code Online (Sandbox Code Playgroud)

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

<test2>
  <alpha bar="xyz"/>
  <beta bar="abc"/>
  <gamma bar="xyz"/>
</test2>
Run Code Online (Sandbox Code Playgroud)


Tom*_*sky 17

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

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

getElementById javadoc:

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

注意:除非如此定义,否则名称为"ID"或"id"的属性不是ID类型.

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