Java XML DOM: как особенные атрибуты id? - PullRequest
19 голосов
/ 06 августа 2010

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

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

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

В документе есть простой старый <p id='fribble'>.

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

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

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

Ответы [ 5 ]

50 голосов
/ 19 сентября 2011

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

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

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

Это легко понять, когда речь идет о схеме XML, поскольку схема XML поддерживает типы данных как для элементов XML, так и для атрибутов XML.Атрибуты XML определены как простые типа (например, xs: строка, xs: целое число, 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 donНе поддерживает богатые типы данных в XML-схеме, он поддерживает ограниченный набор типов атрибутов (который определен в разделе 3.3.1 XML 1.0 ).Обсуждаемые здесь атрибуты определены с типом атрибута из ID.

<!ATTLIST foo  bar ID #IMPLIED>

При использовании вышеуказанной схемы XML или DTD следующий элемент будет идентифицирован значением идентификатораиз «xyz».

<foo bar="xyz"/>

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

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

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

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

В Java getElementById() находит элементы, просматриваядля атрибутов типа ID, а не для атрибутов с name для "id".

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

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

  1. Предоставить схему XML для анализатора ( пример )
  2. Предоставить DTD для анализатора
  3. Явно указатьDOM, который рассматривается как тип атрибута ID.

Третий вариант выполняется с помощью методов setIdAttribute() или setIdAttributeNS() или setIdAttributeNode() в org.w3c.dom.Elementclass .

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 уровня 2 .

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

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

Для соответствия значения типа атрибута ID необходимо использовать функцию XPath id (это одна из Функции набора узлов из XPath 1.0 ):

id("xyz")

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

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

Следует выделить две важные особенности ID.

Во-первых, значения всех атрибутов типа атрибута 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. Поэтому атрибуты с одинаковыми именами атрибутов в разных элементах могут иметь разные свойства type type . В следующем примере XML-документа, если только alpha/@bar имеет тип атрибута идентификатора (и никакого другого атрибута не было), getElementById("xyz") вернет элемент, но getElementById("abc") не будет (так как beta/@bar не имеет атрибута типа ID). Кроме того, атрибут gamma/@bar не может иметь то же значение, что и alpha/@bar, это значение не учитывается в уникальности идентификаторов в документе XML, поскольку оно не имеет тип атрибута ID.

<test2>
  <alpha bar="xyz"/>
  <beta bar="abc"/>
  <gamma bar="xyz"/>
</test2>
17 голосов
/ 06 августа 2010

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

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

Из getElementById javadoc:

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

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

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

4 голосов
/ 06 августа 2010

Атрибут ID не является атрибутом с именем «ID», это атрибут, который объявлен как атрибут ID с помощью DTD или схемы. Например, HTML 4 DTD описывает это:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
3 голосов
/ 06 августа 2010

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

Если вы контролируете запрашиваемый xml, вы также можете попробовать переименовать атрибут в xml:idсогласно http://www.w3.org/TR/xml-id/.

1 голос
/ 26 сентября 2013

Следующее позволит вам получить элемент по идентификатору:

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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...