node - loop xml javascript
Java XML DOM: ¿cómo son especiales los atributos de id? (5)
El atributo de ID no es un atributo cuyo nombre es "ID", es un atributo que se declara como un atributo de ID mediante una DTD o un esquema. Por ejemplo, el html 4 DTD lo describe:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
El javadoc para la clase de Document
tiene la siguiente nota en getElementById
.
Nota: Los atributos con el nombre "ID" o "id" no son de tipo ID a menos que estén definidos
Entonces, leí un documento XHTML en el DOM (usando Xerces 2.9.1).
El documento tiene un antiguo <p id=''fribble''>
en él.
Llamo a getElementById("fribble")
, y devuelve null.
Uso XPath para obtener "// * [id = ''fribble'']", y todo está bien.
Entonces, la pregunta es, ¿qué hace que DocumentBuilder
marque realmente los atributos de ID como ''así definidos''?
Estos atributos son especiales debido a su tipo y no a su nombre .
IDs en XML
Aunque es fácil pensar en los atributos como name="value"
el valor es una cadena simple, no es la historia completa, también hay un tipo de atributo asociado con los atributos.
Esto es fácil de apreciar cuando hay un esquema XML involucrado, ya que el esquema XML admite tipos de datos tanto para elementos XML como para 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 discuten aquí se definen con el tipo de datos incorporado xs:ID
(consulte la sección 3.3.8 de la Parte 2 del Esquema XML: Tipos de datos ).
<xs:element name="foo">
<xs:complexType>
...
<xs:attribute name="bar" type="xs:ID"/>
...
</xs:complexType>
</xs:element>
Aunque DTD no admite los tipos de datos ricos en el esquema XML, sí admite un conjunto limitado de tipos de atributos (que se define en la sección 3.3.1 de XML 1.0 ). Los atributos que se discuten aquí se definen con un tipo de atributo de ID
.
<!ATTLIST foo bar ID #IMPLIED>
Con el esquema XML o DTD anterior, el siguiente elemento será identificado por el valor de ID de "xyz".
<foo bar="xyz"/>
Sin saber el esquema XML o DTD, no hay forma de saber qué es una identificación y qué no lo es:
- Los atributos con el nombre de "id" no tienen necesariamente 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, el xml:id
se inventó posteriormente (consulte la recomendación de Wml / xml: id ). Este es un atributo que siempre tiene el mismo prefijo y nombre, y está destinado a ser tratado como un atributo con el tipo de atributo de ID. Sin embargo, si depende de si el analizador que se está utilizando es consciente de xml:id
o no. Dado que muchos analizadores se escribieron inicialmente antes de que se definiera xml:id
, es posible que no sea compatible.
IDs en Java
En Java, getElementById()
busca elementos buscando atributos de tipo ID, no atributos con el nombre de "id".
En el ejemplo anterior, getElementById("xyz")
devolverá ese elemento foo
, aunque el nombre del atributo no sea "id" (suponiendo que el DOM sepa que la bar
tiene un tipo de atributo de ID).
Entonces, ¿cómo sabe el DOM qué tipo de atributo tiene un atributo? Hay tres maneras:
- Proporcionar un esquema XML al analizador ( example )
- Proporcionar un DTD al analizador
- Indique explícitamente al DOM que se trata como un tipo de atributo de ID.
La tercera opción se realiza con los setIdAttribute()
o setIdAttributeNS()
o setIdAttributeNode()
en la clase 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");
Esto se debe hacer para cada nodo de elemento que tenga uno de estos tipos de atributos en ellos. No existe un método integrado simple para hacer que todas las ocurrencias de atributos con un nombre dado (por ejemplo, "id") sean de ID de tipo de atributo .
Este tercer enfoque solo es útil en situaciones en las que el código que llama a getElementById()
es independiente del que creó el DOM. Si era el mismo código, ya ha encontrado el elemento para configurar el atributo ID, por lo que es poco probable que deba llamar a getElementById()
.
Además, tenga en cuenta que esos métodos no estaban en la especificación DOM original. El getElementById
se introdujo en el nivel 2 de DOM .
IDs en XPath
La XPath en la pregunta original dio un resultado porque solo coincidía con el nombre del atributo.
Para coincidir con los valores de ID de tipo de atributo , se debe usar la función de id
XPath (es una de las funciones de conjunto de nodos de XPath 1.0 ):
id("xyz")
Si se hubiera utilizado, el XPath habría dado el mismo resultado que getElementById()
(es decir, no se encontró ninguna coincidencia).
IDs en XML continuado
Dos características importantes de la identificación deben ser resaltadas.
En primer lugar, los valores de todos los atributos de ID de tipo de atributo deben ser únicos para todo el documento XML . En el siguiente ejemplo, si personId
y personId
tienen un tipo de atributo de ID, sería un error agregar otra compañía con companyId de id24601, porque será un duplicado de un valor de ID existente. Aunque los nombres de los atributos son diferentes, lo que importa es el tipo de atributo .
<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 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 tipo de atributo . En el siguiente documento XML de ejemplo, si solo alpha/@bar
tiene un tipo de atributo de ID (y no hubo otro atributo), getElementById("xyz")
devolverá un elemento, pero getElementById("abc")
no lo hará (desde la beta/@bar
no es de tipo ID de atributo ). 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 singularidad de las ID en el documento XML porque no es de tipo ID de atributo .
<test2>
<alpha bar="xyz"/>
<beta bar="abc"/>
<gamma bar="xyz"/>
</test2>
La expresión xpath correspondiente sería en realidad id(''fribble'')
, que debería devolver el mismo resultado que getElementById
. Para que esto funcione, el dtd o el esquema asociado con su documento deben declarar el atributo como de tipo ID.
Si tiene el control del xml consultado, también puede intentar cambiar el nombre del atributo a xml:id
según http://www.w3.org/TR/xml-id/ .
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;
}
}
Para que la llamada getElementById()
funcione, el Document
debe conocer los tipos de sus nodos, y el nodo de destino debe ser del tipo de ID XML para que el método pueda 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
sea del tipo de ID XML, getElementById()
nunca lo encontrará.
Supongo que su documento no sabe que el atributo id
del elemento p
es del tipo de ID XML (¿verdad?). Puede navegar hasta el nodo en el DOM usando getChildNodes()
y otras funciones transversales del DOM, e intente llamar a Attr.isId()
en el atributo id para asegurarnos.
Desde el javadoc getElementById :
Se espera que la implementación del DOM use el atributo Attr.isId para determinar si un atributo es de tipo ID.
Nota: Los atributos con el nombre "ID" o "id" no son de tipo ID a menos que estén definidos.
Si está utilizando un DocumentBuilder
para analizar su XML en un DOM, asegúrese de llamar a setSchema(schema)
en el DocumentBuilderFactory antes de llamar a newDocumentBuilder (), para asegurarse de que el constructor que obtiene de la fábrica conoce los tipos de elementos.