2010-08-06 11 views
18

El javadoc para la clase Document tiene la siguiente nota debajo de getElementById.Java XML DOM: ¿cómo son los Atributos id especiales?

Nota: atributos con el nombre de "ID" o "id" no son de tipo ID a menos que así se define

Así, leí un documento XHTML en el DOM (usando Xerces 2.9.1) .

El documento tiene un simple viejo <p id='fribble'> en él.

Llamo a getElementById("fribble"), y devuelve nulo.

Uso XPath para obtener "// * [id = 'fribble']", y todo está bien.

Entonces, la pregunta es, ¿qué hace que DocumentBuilder realmente marque los atributos de ID como 'así se define?'

Respuesta

16

Para la llamada al trabajo getElementById(), el Document tiene que conocer los tipos de sus nodos, y el nodo de destino debe ser del tipo de ID XML para el método para encontrarlo. Conoce los tipos de sus elementos a través de un esquema asociado. Si el esquema no está establecido o no declara que el atributo id tiene el tipo de ID XML, getElementById() nunca lo encontrará.

Supongo que su documento no sabe que el atributo p del elemento id es del tipo de identificación XML (¿verdad?). Puede navegar hasta el nodo en el DOM usando getChildNodes() y otras funciones DOM-transversal, e intente llamar al Attr.isId() en el atributo id para asegurarse.

Desde el getElementById javadoc:

Se espera que la implementación DOM a utilizar el atributo Attr.isId a determinar si un atributo es de tipo ID.

Nota: Los atributos con el nombre "ID" o "id" no son del tipo ID a menos que se haya definido .

Si está utilizando un DocumentBuilder para analizar el código XML en un DOM, asegúrese de llamar setSchema(schema) en el DocumentBuilderFactory antes de llamar newDocumentBuilder(), para asegurar que el constructor que recibe de la fábrica es consciente de los tipos de elementos .

4

El atributo de ID no es un atributo cuyo nombre es "ID", es un atributo que se declara como un atributo de ID por un DTD o un esquema. Por ejemplo, el DTD html 4 describe:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
3

la expresión XPath correspondiente sería en realidad id('fribble'), que debe devolver el mismo resultado que getElementById. Para que esto funcione, el dtd o esquema asociado con su documento debe declarar el atributo como de tipo ID.

Si tiene el control del xml consultado, también puede intentar cambiarle el nombre al atributo xml:id según http://www.w3.org/TR/xml-id/.

47

Estos atributos son especiales debido a su tipo y no a causa de su nombre.

ID en XML

Aunque es fácil pensar en atributos como name="value" con el valor está siendo una cadena simple, que no es la historia completa - también hay un tipo atributo asociado con atributos.

Esto es fácil de apreciar cuando se trata de un esquema XML, ya que XML Schema admite tipos de datos para los elementos XML y los atributos XML. Los atributos XML se definen como de un tipo simple (por ejemplo, xs: string, xs: integer, xs: dateTime, xs: anyURI). Los atributos que se tratan aquí se definen con el tipo de datos incorporado xs:ID (consulte 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> 

Aunque DTD no son compatibles con los tipos de datos ricos en esquemas XML, soporta un conjunto limitado de tipos de atributos (que se define en section 3.3.1 of XML 1.0). Los atributos que se tratan aquí se definen con un tipo de atributo de ID.

<!ATTLIST foo bar ID #IMPLIED> 

ya sea con el esquema XML encima o DTD, el elemento siguiente será identificado por el valor de ID de "xyz".

<foo bar="xyz"/> 

Sin conocer el esquema XML o DTD, no hay manera de saber lo que es un ID y lo que no se:

  • atributos con el nombre de "id" no necesariamente tienen un tipo de atributo de ID; y
  • ¡Los atributos con nombres que no son "id" pueden tener un tipo de atributo de ID!

Para mejorar esta situación, se inventó posteriormente el xml:id (ver xml:id W3C Recommendation). Este es un atributo que siempre tiene el mismo prefijo y nombre, y está destinado a tratarse como un atributo con tipo de atributo de ID. Sin embargo, si lo hace dependerá de que el analizador utilizado sea consciente de xml:id o no. Dado que muchos analizadores sintácticos se escribieron inicialmente antes de que se definiera el xml:id, es posible que no sea compatible.

identificadores en Java

En Java, getElementById() encuentra elementos mediante la búsqueda de atributos de tipo ID, no para los atributos con el nombre de "id".

En el ejemplo anterior, getElementById("xyz") volverá ese elemento foo, aunque el nombre del atributo en él no es "id" (suponiendo que el DOM sabe que bar tiene un tipo de atributo de ID).

Entonces, ¿cómo sabe el DOM qué atributo tipo tiene un atributo? Hay tres maneras:

  1. proporcionar un esquema XML con el analizador (example)
  2. Proporcionar una DTD para el analizador
  3. indicar explícitamente a la DOM que se trata como un tipo de atributo de ID.

La tercera opción se realiza utilizando los setIdAttribute() o setIdAttributeNS() o setIdAttributeNode() métodos en el org.w3c.dom.Element class.

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"); 

Esto tiene que hacerse para cada nodo de elemento que tenga uno de estos tipos de atributos en ellos. No existe un método simple incorporado para hacer que todas las apariciones de atributos con un nombre de pila (por ejemplo, "id") sean de tipo de atributo ID.

Este tercer enfoque sólo es útil en situaciones donde el código llamando a la getElementById() será independiente de que la creación de la DOM. Si era el mismo código, ya ha encontrado el elemento para establecer el atributo ID por lo que es poco probable que necesite llamar al getElementById().

Además, tenga en cuenta que esos métodos no estaban en la especificación DOM original. El getElementById se introdujo en DOM level 2.

ID en XPath

El XPath en la pregunta original dio resultado, ya que sólo se pongan en venta el atributo de nombre .

para que coincida con el tipo valores de ID de atributos, la función XPath id necesita ser utilizado (que es uno de los Node Set Functions from XPath 1.0):

id("xyz") 

Si ese hubiera sido utilizado, el XPath habría dado el mismo resultado que getElementById() (es decir, no se encontraron coincidencias).

ID en XML continuaron

Dos características importantes de ID deben ser resaltados.

En primer lugar, los valores de todos los atributos de tipo de atributo ID debe ser única para todo el documento XML. En el siguiente ejemplo, si personId y companyId tienen atributo tipo de ID, sería un error agregar otra compañía con companyId de id24601, porque será un duplicado de un valor de ID existente. A pesar de que los nombres de los atributos son diferentes, es el tipo de atributo que importa.

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

En segundo lugar, los atributos se definen en los elementos en lugar de todo el documento XML. Por lo tanto, los atributos con el mismo nombre de atributo en diferentes elementos pueden tener diferentes propiedades de de tipo de atributo. En el siguiente documento XML de ejemplo, aunque sólo sea alpha/@bar tiene un tipo de atributo de Identificación (y ningún otro atributo era), getElementById("xyz") volverá un elemento, pero no lo hará getElementById("abc") (ya que no es beta/@bar de tipo de atributo ID). Además, no es un error que el atributo gamma/@bar tenga el mismo valor que alpha/@bar, ese valor no se considera en la exclusividad de los ID en el documento XML porque no es de ID de tipo de atributo.

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

+1 respuesta Niza! Aprendí algo nuevo hoy. –

+0

Desafortunadamente, el enlace al ejemplo del esquema ya está muerto. – ferkulat

1

Lo siguiente le permitirá obtener un elemento por 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; 
    } 
} 
Cuestiones relacionadas