Коротко о DTD

Введение

Спецификация SGML предусматривает создание новых языков разметки.
HTML и XML как раз таковыми и являются. Причём XML сам по себе предусматривает расширяемость. Документы созданные с помощью этих языков могут быть «корректными (well-formed)» и «допустимыми (valid)».

С проверкой документа на корректность проблем не возникает: если ошибок не выскочило и всё отобразилось так, как мы хотели, то документ корректен. Например, если в HTML-документе написать что-то вроде «<Z>Привет!</Z>», то наш документ будет полностью корректен, но проигнорирован браузером. Почему? Потому что браузер ничего не знает о том, что это за «Z» такой. И если мы проверим наш документ на допустимость с помощью валидатора, то документ таковым признан не будет. А как об этом узнает валидатор и на основании чего он вынес такой вердикт?

Допустимость проверяется с помощью определения типа документа (DTD, document type definition). Например, для «строгого» HTML он выглядит так.

DTD может быть описан как внутри документа, так и вынесен в отдельный файл (аналогия с CSS: встроенные и подключаемые таблицы стилей).

Объявление DTD

Объявление DTD располагается перед первым (корневым) элементом документа, начинается с последовательности «<!DOCTYPE» и заканчивается символом «>».

Внутреннее DTD описывается так:

<!DOCTYPE catalog [

]
>

Между квадратными скобками располагается содержимое DTD, так называемое внутреннее подмножество, например:

<!DOCTYPE catalog [
    <!ELEMENT someelement (#PCDATA)>
]
>

Если DTD вынесено в отдельный файл (обычно имеющий расширение .dtd), то его объявление в документе записывается так:

<!DOCTYPE catalog SYSTEM "catalog.dtd">

Соответственно, в этом файле и прописываются все правила, так называемое внешнее подмножество.

Имя, указанное за словом «DOCTYPE» (в нашем случае «catalog»), должно соответствовать имени корневого элемента. То есть, XML-документ должен быть примерно таким:

<?xml version="1.0"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
  <someelement>Hello, world!</someelement>
</catalog>

Вместо ключевого слова «SYSTEM» может быть использовано слово «PUBLIC», указывающее, что DTD применяется для широкого использования:

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

Внутренние и внешние подмножества могут быть заданы одновременно (опять же, аналогия с CSS):

<!DOCTYPE catalog SYSTEM "catalog.dtd"[
    <!ELEMENT someelement (#PCDATA)>
]
>

Здесь, сначала зачитывается содержимое файла «catalog.dtd», а потом содержимое, указанное внутри квадратных скобок.

Элементы документа

Элементы объявляются в DTD с помощью ключевого слова «ELEMENT», после которого следует имя элемента и его содержимое заключенное в круглые скобки :

<ELEMENT book (#PCDATA)>

Если у элемента есть дочерние элементы, то это записывается так:

<!ELEMENT book (title)>

что соответствует документу:

<book>
    <title />
</book>

Если у элемента несколько дочерних элементов, то они перечисляются через запятую и должны следовать строго в указанном порядке:

<!ELEMENT book (title, author)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>

Приведённый выше пример читается следующим образом. Элемент «book» должен содержать только один элемент «title», за которым должен следовать только один элемент «author». Сами элементы «title» и «author» никаких элементов не содержат, а могут содержать лишь какой-нибудь текст.

С помощью следующих специальных символов можно определять количественное присутствие элемента:

  • Символ «*», следующий после элемента, означает, что элемент может присутствовать один или несколько раз, или не присутствовать вовсе(от нуля до + бесконечности)
  • Символ «+», следующий после элемента, означает, что элемент может присутствовать один или несколько раз(от 1 до + бесконечности)
  • Символ «?», следующий после элемента, означает, что элемент может либо отсуствовать, либо присутствовать только один раз(0 или 1)
<!ELEMENT book (title, author)>
...
<!ELEMENT book (title*, author)>
...
<!ELEMENT book (title+, author)>
...
<!ELEMENT book (title?, author)>

Если существует необходимость указать один из нескольких элементов (или title, или author — любой из них, но не оба), надо испольовать символ «|»:

<!ELEMENT book (title | author)>

Текст тоже равноправный участник игры. Ключевое слово «PCDATA» указывает на анализируемые символьные данные, поэтому любой текст содержащий символы разметки («<», «>» и «&») будет трактоваться как разметка. Совместное использование текста и элементов называется смешанным содержимым. При объявлении смешанного содержимого, «PCDATA» необходимо указывать первым:

<!ELEMENT book (#PCDATA | title | author)>

Следующий фрагмент документа валиден вышеприведенному примеру:

<book>
    <title />
</book>
<book>
    <author />
</book>
<book>
    Нет данных
</book>

Группы элементов заключаются в круглые скобки. Элемент «book» должен содержать либо текст, либо (один «title», один или неколько «author» и может быть один «pubyear» именно в таком порядке):

<!ELEMENT book (#PCDATA | (title, author+, pubyear?))>
<!ELEMENT disc (#PCDATA | title)*>

Данному примеру соответствует следующий фрагмент XML-документа:

<disc />
<book>
    <title />
    <author />
    <author />
</book>
<book>
    Нет данных
</book>

Элемент может быть пустым. Такой элемент не может содержать не дочерних элементов ни текста (например, элемент «br» в HTML). Такой элемент задается с ключевым словом «EMPTY»:

<!ELEMENT photo EMPTY>

Элемент также может быть объявлен с ключевым словом «ANY» означающее, что элемент может содержать и элементы, и текст, и все это вместе, и даже быть пустым.

Атрибуты элементов

Элементы в XML-документе могут иметь атрибуты, которые записываются в виде «имя = значение» в открывающем или пустом тегах. Общее объявление атрибутов конкретного элемента начинается с ключевого слова «ATTLIST», после которого следует имя данного элемента и объявления самих атрибутов:

<!ELEMENT title (#PCDATA)>
    <!ATTLIST title
           id CDATA #REQUIRED
           stock CDATA #IMPLIED>

Ключевое слово «REQUIRED» указывает на то, что атрибут обязателен. Ключевое слово «IMPLIED», наоборот, говорит, что атрибут необязателен.

У атрибутов могут быть перечисленны разрешенные значения:

<!ATTLIST title
       readonly (yes|no) #REQUIRED>

Также может быть задано значение по-умолчанию:

<!ATTLIST title
       readonly (yes|no) #REQUIRED "yes">
       pubyear CDATA #IMPLIED "2007">

Атрибут может быть и константой, то есть у него может быть только то значение, которое заявлено в объявлении атрибута. Делается это с помощью ключевого слова «FIXED»:

<!ATTLIST title
       copyright <strong>#FIXED "udvikler">

Тип атрибута «CDATA»позволяет использовать любые символы кроме «<», «>», «&», «"» и «'». В случае использования, данные символы должны быть заменены на спецсимволы типа «&lt;» и т.п. Внимание: в DTD ключевое слово «CDATA» имеет другое значение, чем раздел «CDATA» в XML-документе!

Помимо типа CDATA, атрибуты могут иметь следующие типы:

  • NMTOKEN — может содержать только буквы, цифры, «.», «-», «_» и «:»
  • NMTOKENS — может содержать те же символы, что и «NMTOKEN», а также символы пробела, возврата каретки, перевода строки и табуляции
<!ATTLIST title
       id CDATA #REQUIRED
       stock NMTOKEN #IMPLIED
       publisher NMTOKENS #IMPLIED>

Еще один тип атрибута «ID» разрешает задавать те же значения, что и тип NMTOKEN, но начинаться значение должно либо с буквы, либо с «_», либо с «:». У любого элемента может быть только один атрибут с типом «ID». Атрибут типа «ID» не может быть константой (объявляться как «FIXED»). Значение атрибута типа «ID» должно быть уникальным для всего XML-документа:

<!ELEMENT book (title, author)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
    <!ATTLIST title
           id ID #REQUIRED
           stock CDATA #IMPLIED>
    <!ATTLIST author
           id ID #REQUIRED
           city CDATA #IMPLIED>

Атрибут элемента может быть ссылкой на атрибут типа «ID» другого элемента. Для этого он объявляется как атрибут типа «IDREF». Если атрибут должен ссылаться на атрибут типа «ID» нескольких элементов, то испольуется ключевое слово «IDREFS»:

<!ELEMENT book (title+, author, year, publisher)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT author (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT publisher (#PCDATA)>
    <!ATTLIST title
           id ID #REQUIRED>
    <!ATTLIST author
           id ID #REQUIRED>
    <!ATTLIST year
           ref IDREF #REQUIRED>
    <!ATTLIST publisher
           from IDREFS #REQUIRED>

В XML-документе это будет выглядить так:

<book>
    <title id="t1" />
    <title id="t2" />
    <title id="t3" />
    <author id="a1" />
    <year ref="t2" />
    <publisher from="t1 t2 a1" />
</book>

Объявление сущностей

Помимо элементов и их атрибутов, мы можем определить сущности, записываемые с помощью ключевого слова «ENTITY»:

<!ENTITY name "SuperMegaMaster">

В XML-документе ссылка на эту сущность определяется следующим образом (амперсанд + имя сущности + точка с запятой):

<userName>&name;</userName>

В результате чего, на место имени сущности «name», будет подставлено ее значение, в нашем случае — «SuperMegaMaster».

И для полноты нашего счастья, надо добавить, что атрибуты элементов могут иметь в качестве значения подобные сущности — сущности-атрибуты. Они тоже определяются с помощью ключевого слова «ENTITY», но имеют одно ограничение — они должны ссылаться на внешние неанализируемые сущности, определенные во внешнем подмножестве DTD:

<!DOCTYPE catalog [
    <!ENTITY list SYSTEM "companyList.html" NDATA parse>
    <!ELEMENT catalog (user+)>
    <!ELEMENT user (name)>
        <!ATTLIST user company ENTITY #REQUIRED>
    <!ELEMENT name (#PCDATA)>
]
>

В вышеприведённом примере, объявлена сущность «list», которая ссылается на внешний документ «companyList.html». Ключевое слово «NDATA», говорит о том, что внешний документ неявляется XML-документом. Далее, для элемента «user» объявляется атрибут «company», который является обязательным и имеет тип «ENTITY», то есть ссылается на какую-либо сущность. Поскольку в нашем пример задана только одна сущность («list»), то именно она и только она может быть значением атрибута «company» в XML-документе:

<catalog>
    <user company="list">
        <name>SuperMegaMaster</name>
    </user>
</catalog>

Осталось только понять, что означает «parse» в строке объявления сущности «list»? Когда используются неанализируемые данные, то есть те, которые не анализируются синтаксическим анализатором XML, хорошо было бы дать информацию приложению (использующему данный XML-документ), каким образом обработать эту сущность, если все-таки потребуется. Для этого нужно использовать нотацию, задаваемую ключевым словом «NOTATION» и дополнить наш DTD следующим образом:

<!NOTATION parse SYSTEM "iexlorer">
<!ENTITY list SYSTEM "companyList.html" NDATA parse>

Слово «parse» в объявлении сущности лист указывает на то, каким образом можно проанализировать файл «companyList.html» — найти нотацию с именем «parse» и следовать ее указаниям. В нашем случае, приложение может открыть MS InternetExplorer и загрузить в него документ «companyList.html».