Подтвердить что ты не робот

Java XML DOM: как специальные атрибуты id?

javadoc для класса Document имеет следующее примечание под getElementById.

Примечание. Атрибуты с именем "ID" или "id" не имеют идентификатора типа, если они не определены

Итак, я прочитал документ XHTML в DOM (используя Xerces 2.9.1).

Док имеет простой старый <p id='fribble'> в нем.

Я вызываю getElementById("fribble"), и он возвращает null.

Я использую XPath для получения "//* [id = 'fribble']", и все хорошо.

Итак, вопрос в том, что заставляет DocumentBuilder фактически отмечать атрибуты идентификатора как "так определенные?"

4b9b3361

Ответ 1

Для вызова getElementById() для работы, Document должен знать типы своих узлов, а целевой node должен быть типа идентификатора XML для метода, чтобы найти его. Он знает о типах своих элементов через связанную схему. Если схема не установлена ​​или не объявляет атрибут id типа XML ID, getElementById() никогда не найдет его.

Я предполагаю, что ваш документ не знает, что атрибут p element id имеет тип идентификатора XML (это?). Вы можете перейти к node в DOM, используя getChildNodes() и другие функции обхода DOM, и попробуйте вызвать Attr.isId() в атрибуте id, чтобы точно сказать.

Из getElementById javadoc:

Ожидается, что реализация DOM используйте атрибут Attr.isId для определить, является ли атрибут типа ID.

Примечание. Атрибуты с именем "ID" или "id" не имеют идентификатора типа, если определены.

Если вы используете DocumentBuilder для анализа вашего XML в DOM, обязательно вызывайте setSchema(schema) в DocumentBuilderFactory перед вызовом newDocumentBuilder(), чтобы гарантировать, что строитель, который вы получаете из factory, знает типы элементов.

Ответ 2

Эти атрибуты являются особыми из-за их типа, а не из-за их имени .

Идентификаторы в XML

Хотя легко найти атрибуты как name="value" со значением, являющимся простой строкой, это не полная история - также существует атрибут , связанный с атрибутами.

Это легко понять, когда есть XML-схема, поскольку XML Schema поддерживает типы данных как для XML-элементов, так и для XML-атрибутов. Атрибуты XML определены как простые типы (например, xs: string, xs: integer, xs: dateTime, xs: anyURI). Атрибуты, обсуждаемые здесь, определяются с помощью встроенного типа данных xs:ID (см. раздел 3.3.8 XML-схемы, часть 2: Типы данных).

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

Хотя DTD не поддерживает богатые типы данных в XML-схеме, он поддерживает ограниченный набор типов атрибутов (который определен в разделе раздел 3.3.1 XML 1.0). Обсуждаемые здесь атрибуты определяются с типом атрибута ID.

<!ATTLIST foo  bar ID #IMPLIED>

С помощью приведенной выше схемы XML или DTD следующий элемент будет идентифицироваться значением ID "xyz".

<foo bar="xyz"/>

Не зная Схемы XML или DTD, невозможно определить, что такое идентификатор, а что нет:

  • Атрибуты с именем "id" необязательно имеют тип атрибута ID; и
  • Атрибуты с именами, которые не являются "id" , могут иметь тип идентификатора ID!

Чтобы улучшить эту ситуацию, впоследствии был изобретен xml:id (см. xml: id W3C Рекомендация). Это атрибут, который всегда имеет один и тот же префикс и имя и предназначен для обработки как атрибута с типом атрибута ID. Тем не менее, независимо от того, будет ли он зависеть от используемого анализатора, известно о xml:id или нет. Поскольку многие парсеры были первоначально записаны до того, как был определен xml:id, он может не поддерживаться.

Идентификаторы в Java

В Java getElementById() находит элементы, ища атрибуты идентификатора type, а не для атрибутов с именем "id" .

В приведенном выше примере getElementById("xyz") вернет этот элемент foo, даже если имя атрибута на нем не является "id" (если DOM знает, что bar имеет тип атрибута ID).

Итак, как DOM знает, какой атрибут имеет атрибут? Существует три способа:

  • Предоставьте XML-схему парсеру (пример)
  • Предоставление DTD для анализатора
  • Явно указать DOM, что он рассматривается как тип атрибута идентификатора.

Третий вариант выполняется с использованием методов setIdAttribute() или setIdAttributeNS() или setIdAttributeNode() в 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");

Это нужно сделать для каждого элемента node, который имеет на них один из этих типов атрибутов. Нет простого встроенного метода, чтобы все вхождения атрибутов с заданным именем (например, "id" ) имели идентификатор типа атрибута.

Этот третий подход полезен только в ситуациях, когда код, вызывающий getElementById(), отделен от того, что создает DOM. Если это был тот же код, он уже нашел элемент для установки атрибута ID, поэтому вряд ли потребуется вызвать getElementById().

Кроме того, имейте в виду, что эти методы не были в исходной спецификации DOM. getElementById был введен в уровень DOM уровня 2.

Идентификаторы в XPath

XPath в исходном вопросе дал результат, потому что он соответствовал только имени атрибута.

Чтобы соответствовать значениям идентификатора типа атрибута, необходимо использовать функцию XPath ID (это один из Node Установить функции из XPath 1.0):

id("xyz")

Если бы это было использовано, XPath дал бы тот же результат, что и getElementById() (т.е. совпадение не найдено).

Идентификаторы в XML продолжаются

Следует выделить две важные функции ID.

Во-первых, значения всех атрибутов идентификатора типа атрибута должны быть уникальными для всего XML-документа. В следующем примере, если personId и companyId оба имеют тип идентификатора атрибута, было бы ошибкой добавлять другую компанию с companyId id24601, поскольку она будет дублировать существующее значение идентификатора. Несмотря на то, что имена атрибутов различны, это относится к типу атрибутов.

<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") не будет (поскольку beta/@bar не относится к типу атрибута Я БЫ). Кроме того, для атрибута gamma/@bar не является ошибкой иметь то же значение, что и alpha/@bar, это значение не учитывается в уникальности идентификаторов в документе XML, поскольку оно не относится к идентификатору типа атрибута.

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

Ответ 3

Идентификатор атрибута не является атрибутом, чье имя является "ИД", это атрибут, объявленный как атрибут идентификатора DTD или схемой. Например, DTD html 4 описывает это:

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

Ответ 4

Соответствующее выражение xpath фактически будет id('fribble'), которое должно возвращать тот же результат, что и getElementById. Для этого dtd или схема, связанная с вашим документом, должны объявить атрибут как идентификатор типа.

Если вы контролируете запрошенный xml, вы также можете попробовать переименовать атрибут xml:id в соответствии с http://www.w3.org/TR/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;
    }
}