Java XML DOM:id 属性有啥特别之处?
Posted
技术标签:
【中文标题】Java XML DOM:id 属性有啥特别之处?【英文标题】:Java XML DOM: how are id Attributes special?Java XML DOM:id 属性有什么特别之处? 【发布时间】:2011-03-26 07:18:21 【问题描述】:Document
类的 javadoc 在getElementById
下有以下注释。
注意:除非如此定义,否则名为“ID”或“id”的属性不属于 ID 类型
所以,我将 Xhtml 文档读入 DOM(使用 Xerces 2.9.1)。
文档中有一个普通的旧 <p id='fribble'>
。
我调用getElementById("fribble")
,它返回null。
我使用 XPath 获取“//*[id='fribble']”,一切正常。
那么,问题是,是什么导致DocumentBuilder
实际上将 ID 属性标记为“如此定义”?
【问题讨论】:
【参考方案1】:这些属性之所以特殊是因为它们的类型,而不是因为它们的名称。
XML 中的 ID
虽然很容易将属性视为name="value"
,其值是一个简单的字符串,但这并不是全部——还有一个与属性相关的属性类型。
当涉及到 XML Schema 时,这很容易理解,因为 XML Schema 支持 XML 元素和 XML 属性的数据类型。 XML 属性被定义为简单类型(例如 xs:string、xs:integer、xs:dateTime、xs:anyURI)。这里讨论的属性是使用xs:ID
内置数据类型定义的(请参阅section 3.3.8 of the XML Schema Part 2: Datatypes)。
<xs:element name="foo">
<xs:complexType>
...
<xs:attribute name="bar" type="xs:ID"/>
...
</xs:complexType>
</xs:element>
虽然 DTD 不支持 XML Schema 中的丰富数据类型,但它确实支持有限的一组属性类型(在section 3.3.1 of XML 1.0 中定义)。这里讨论的属性是用ID
的属性类型定义的。
<!ATTLIST foo bar ID #IMPLIED>
使用上述 XML Schema 或 DTD,以下元素将由 ID 值“xyz”标识。
<foo bar="xyz"/>
如果不知道 XML Schema 或 DTD,就无法分辨什么是 ID,什么不是:
名称为“id”的属性不一定有ID的属性类型;和 名称不是“id”的属性可能具有 ID 的属性类型!为了改善这种情况,随后发明了xml:id
(参见xml:id W3C Recommendation)。这是一个始终具有相同前缀和名称的属性,旨在被视为 ID 为 attribute type 的属性。但是,它是否会取决于正在使用的解析器是否知道xml:id
。由于许多解析器最初是在定义 xml:id
之前编写的,因此可能不受支持。
Java 中的 ID
在 Java 中,getElementById()
通过查找 type ID 的属性而不是 name 为“id”的属性来查找元素。
在上面的例子中,getElementById("xyz")
将返回 foo
元素,即使它上面的属性名称不是“id”(假设 DOM 知道 bar
具有 属性类型 的 ID)。
那么 DOM 是如何知道一个属性有什么属性类型的呢?有三种方式:
-
向解析器提供 XML Schema (example)
向解析器提供 DTD
向 DOM 明确指出它被视为 ID 的属性类型。
第三个选项是使用org.w3c.dom.Element
class 上的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。
这第三种方法仅在调用getElementById()
的代码与创建DOM 的代码分开的情况下有用。如果是相同的代码,它已经找到了设置ID属性的元素,所以不太可能需要调用getElementById()
。
另外,请注意,这些方法不在原始 DOM 规范中。 getElementById
是在 DOM level 2 中引入的。
XPath 中的 ID
原始问题中的 XPath 给出了结果,因为它只匹配属性 name。
要匹配属性类型 ID 值,需要使用 XPath id
函数(它是 Node Set Functions from XPath 1.0 之一):
id("xyz")
如果已使用,XPath 将给出与 getElementById()
相同的结果(即未找到匹配项)。
XML 中的 ID(续)
应突出显示 ID 的两个重要特征。
首先,属性类型ID的所有属性的值必须对整个XML文档是唯一的。在以下示例中,如果 personId
和 companyId
的 attribute type 为 ID,则添加另一个 ID 为 id24601 的 companyId
的公司将是错误的,因为它将与现有的 ID 值。即使属性名称不同,重要的是 属性类型。
<test1>
<person personId="id24600">...</person>
<person personId="id24601">...</person>
<company companyId="id12345">...</company>
<company companyId="id12346">...</company>
</test1>
其次,属性是在元素上定义的,而不是整个 XML 文档。因此,不同元素上具有相同属性名称的属性可能具有不同的 属性类型 属性。在以下示例 XML 文档中,如果只有 alpha/@bar
具有 ID 的 属性类型(并且没有其他属性),getElementById("xyz")
将返回一个元素,但 getElementById("abc")
不会(因为 @ 987654359@ 不是属性类型 ID)。此外,属性gamma/@bar
与alpha/@bar
具有相同的值也不是错误,因为它不是属性类型 em> 身份证。
<test2>
<alpha bar="xyz"/>
<beta bar="abc"/>
<gamma bar="xyz"/>
</test2>
【讨论】:
不幸的是,模式示例的链接现在已经失效了。【参考方案2】:要使getElementById()
调用起作用,Document
必须知道其节点的类型,并且目标节点必须是 XML ID 类型,以便方法找到它。它通过关联的模式了解其元素的类型。如果未设置架构,或未将 id
属性声明为 XML ID 类型,getElementById()
将永远找不到它。
我的猜测是您的文档不知道p
元素的id
属性属于XML ID 类型(是吗?)。您可以使用getChildNodes()
和其他DOM 遍历函数导航到DOM 中的节点,并尝试在id 属性上调用Attr.isId()
来确定。
来自getElementByIdjavadoc:
DOM 实现预计将 使用属性 Attr.isId 来 确定属性是否属于类型 身份证。
注意:名称为“ID”的属性或 "id" 不是 ID 类型,除非是这样 已定义。
如果您使用DocumentBuilder
将您的 XML 解析为 DOM,请务必在调用 newDocumentBuilder() 之前在 DocumentBuilderFactory 上调用 setSchema(schema)
,以确保您从工厂获得的构建器知道元素类型.
【讨论】:
【参考方案3】:ID 属性不是名称为“ID”的属性,它是由 DTD 或模式声明为 ID 属性的属性。例如,html 4 DTD 对其进行了描述:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
【讨论】:
【参考方案4】:对应的 xpath 表达式实际上是id('fribble')
,它应该返回与getElementById
相同的结果。为此,与您的文档关联的 dtd 或架构必须将属性声明为类型 ID。
如果您可以控制查询的 xml,您还可以尝试按照 http://www.w3.org/TR/xml-id/ 将属性重命名为 xml:id
。
【讨论】:
【参考方案5】:以下将允许您通过 id 获取元素:
public static Element getElementById(Element rootElement, String id)
try
String path = String.format("//*[@id = '%1$s' or @Id = '%1$s' or @ID = '%1$s' or @iD = '%1$s' ]", id);
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList)xPath.evaluate(path, rootElement, XPathConstants.NODESET);
return (Element) nodes.item(0);
catch (Exception e)
return null;
【讨论】:
以上是关于Java XML DOM:id 属性有啥特别之处?的主要内容,如果未能解决你的问题,请参考以下文章
java的xml的解析方式有啥,他们的解析流程是怎么样的,有啥区别