Идеальная расширяемость: пространства имён в XML

22 января 2009 года, 19:23

Почти все основанные на SGML приложения содержат в себе массу таинственных, но от этого не менее интересных, вещей, о которых можно часами рассказывать знакомым программистам или девушкам. И те, и другие такие рассказы о скрытых возможностях различных языков разметки слушают довольно внимательно, стараясь не отвлекаться на внешние раздражители. А всё почему? Потому что вдохновители SGML и его потомков сделали всё возможное, чтобы их формат играл не только роль обычного связующего между человеком и машиной, но и являлся средством вдохновения на различные приятные подвиги. Именно поэтому Web-инженеров можно называть азартными романтиками.

Многие Web-разработчики, достигшие третьего уровня сложности игры в HTML (кто уже перешёл или начинает свой переход на строгий XHTML), могут задать вполне логичный вопрос: что это за атрибут такой, xmlns, который можно встретить на самой верхушке документа? Должно быть интересно, что он обозначает и зачем используется в XHTML. Давайте разбираться.

Природа загадочного атрибута

Атрибут xmlns — что же это такое? Этот атрибут можно встретить не только в XHTML, но и в его ближайшем родственнике: XML. Причём, как мы знаем, XML, в отличие от XHTML, не определяет никаких обязательных атрибутов и элементов, предоставляя инженеру полную свободу действий. Однако, даже в XML рассматриваемый нами атрибут выполняет особую роль, хоть он и не обязателен для определения в документе. В чём же заключается роль этого атрибута?

Представьте себе книжную полку, где каждое отделение содержит книги определённого стиля, жанра или автора. Всё на этой полке приведено в полный порядок и гармонию. Это облегчает читателю поиск любимой книги, облегчает ему поиск конкретного автора или выбор определённого жанра из всей коллекции. Особенно такая систематизация помогает при наличии немалого числа книг. Дома громадных книжных коллекций можно редко где увидеть, а вот в библиотеках — это единственный способ держать под контролем армию знаний. Представьте себе библиотеку, где всё было бы смешано между собой, книги сложены в кучу да ещё и в разных местах. Как долго вы бы искали то, зачем пришли?

Атрибут xmlns играет роль книжного стеллажа: он разделяет различные классы элементов и атрибутов, чтобы их легче было применять в документе, чтобы не происходило путанницы. Такая практика применяется не только в языках разметки: можно привести ещё несколько удачных реализаций подобной задумки. Разработчики конкретных языков программирования, например, C++, Java, Ruby или Python, встречаются с понятием «пространство имён» практически каждый день. Причём, в них он играет такую же роль, что и в XML.

Однако почему xmlns называется именно так? Всё просто: xmlns обозначает XML Namespace, то есть «Пространство имён XML».

Определение пространства имён

Определить пространство имён мы можем на любом элементе документа; его эффект будет распространяться на все элементы, вложенные в него и на сам этот элемент. У одного элемента мы можем определять сколь угодно пространств имён. Существует два способа определения пространства имён: префиксный и безпрефиксный.

  1. Префиксный — это такой способ определения пространства имён, при котором мы задаём его имя. При использовании такого пространства имён всегда необходимо применять в дальнейшем расширенний вид имени элемента (или атрибута), чтобы указать на то, к какому пространству имён относится этот элемент (атрибут). Для определения префиксным способом используется следующая конструкция: xmlns:имя_пространства_имён="URI_пространства_имён";
  2. Безпрефиксный способ подразумевает определение пространства имён по умолчанию для имён элементов (и атрибутов), записанных в обычной форме (это и есть та повседневная форма записи имён элементов и атрибутов, которую мы чаще всего используем). Его запись выглядит так же, как и при использовании префиксного способа, но без имени_пространства_имён, то есть: xmlns="URI_пространства_имён".

В каждом из этих способов вы могли видеть понятие URI_пространства_имён в качестве значения атрибута xmlns (или xmlns:имя_пространства_имён). Это самый обычный URI, который соответствует определяемому нами пространству имён. Добавляя в документ новое пространство имён, мы делаем соответствующую привязку всех элементов и атрибутов, использующих его, к определённому адресу в Интернете. Этот адрес указывает на Web-страницу, на которой расположена информация о том, какие элементы и атрибуты может включать в себя данное пространство имён. Совершенно необязательно, чтобы на той странице был страшный документ для обработки машинами; напротив: информация, представленная в нём, должна быть понятна человеку, потому что прежде всего люди используют различные пространства имён при разметке тех или иных документов.

Одно правило должно выполняться для такого типа определений пространства имён: URI должен быть уникален и не может быть пустым (несмотря на то, что пустая строка — это валидный URI). Это значит, что каждое отдельное пространство имён должно быть описано на своей собственной странице, чтобы не возникало никаких пересечений между разными сущностями.

Существует несколько ограничений, накладываемых на добавление новых пространств имён.

  1. Пространство имён с именем xml является зарезервированным и используется по умолчанию для всех XML-документов. Это значит, что если вы создали XML-документ и не определили нигде свои пространства имён, то весь документ использует пространство имён xml;
  2. Пространство имён с именем xmlns также является зарезервированным, так как с помощью него мы определяем другие пространства имён. По умолчанию, атрибут xmlns как раз и является членом данного пространства имён.

Теперь разберёмся с расширенным видом имени элемента. Обычно, мы встречаемся с такой записью элемента:

<element attr_first="value" attr_second="another_value"> Содержимое элемента и/или вложенные в него элементы </element>

Если мы определяем новое пространство имён префиксным способом, то запись имён элементов и атрибутов приобретает следующий вид:

<namespace:element namespace:attr_first="value" namespace:attr_second="another_value"> </namespace:element>

В этом случае мы к имени каждому элемента и атрибута добавляем префикс, состоящий из имени пространства имён и двоеточия. Разумеется, необязательно, чтобы элемент и все его атрибуты были записаны в одном и том же пространстве имён. Элемент может использовать одно пространство имён, атрибуты — другое, а могут и не использовать вовсе.

<element namespace:attr_first="value" attr_second="another_value"> </element>

В вышеприведённом примере элемент element и атрибут attr_second используют пространство имён по умолчанию, а атрибут attr_first — пространство имён namespace.

Применение пространств имён

Данное полезное свойство XML следует применять тогда, когда очевидно, что в документе будут использованы сущности разных видов. Если используемые элементы в документе совершенно рознятся по своему предназначению, либо их парсинг будет происходить по разным критериям, то следует разграничить их в разные пространства имён.

При обработке документов с использованием пространств имён машинными средствами, можно использовать механизмы для предупреждающего вычленения элементов, соответствующих конкретным пространствам имён, чтобы облегчить последующую (возможную) процедуру обработки дерева XML-документа. Такая возможность очень полезна при построении шаблонизаторов, основанных на XML: можно разграничить элементы шаблонизатора и элементы интерфейса, тогда, вкупе с исчерпывающей документацией по каждому из пространств имён, получится отличная дифференцированная система обработки сложных структурированных XML-шаблонов.

Давайте посмотрим на простые примеры. Для начала создадим простой XML-документ:

<?xml version="1.0" encoding="utf-8" ?> <vocabulary> <word> <name>Информация</name> <description>Продукт взаимодействия данных и методов, рассмотренный в контексте этого взаимодействия.</description> </word> <word> <name>Байт</name> <description>Единица измерения количества информации, 8 бит.</description> </word> <word> <name>Бит</name> <description>Один разряд двоичного кода (двоичная цифра).</description> </word> </vocabulary>

Мы создали простой структурированный словарик определений. По умолчанию, мы не определили смысл каждого из наших тегов, потому что не задали пространство имён по умолчанию. Такая информация полезна прежде всего для редакторов подобных документов, так как из пространств имён они могут получить документацию по применяемой разметке. Информация может быть обычной XHTML-страницей, рекомендацией или даже статьёй, где рассказывается об использовании элементов и атрибутов данного пространства имён.

<?xml version="1.0" encoding="utf-8" ?> <vocabulary xmlns="http://example.com/namespace/vocabulary.html"> <word> <name>Информация</name> <description>Продукт взаимодействия данных и методов, рассмотренный в контексте этого взаимодействия.</description> </word> <word> <name>Байт</name> <description>Единица измерения количества информации, 8 бит.</description> </word> <word> <name>Бит</name> <description>Один разряд двоичного кода (двоичная цифра).</description> </word> </vocabulary>

Теперь любой сможет пройти по этой ссылке и просмотреть информацию обо всех элементах, используемых в документе. Теперь усложним ситуацию: представим, что нам понадобилось размечать описание каждого слова XHTML-разметкой. Просто вписать имена соответствующих элементов мы не можем, поскольку мы определили собственное пространство имён для элементов без префиксов (даже если бы мы этого не сделали, по умолчанию для таких элементов используется пространство имён xml). Мы с этой проблемой можем спокойно, без нервов, справиться:

<?xml version="1.0" encoding="utf-8" ?> <vocabulary xmlns="http://example.com/namespace/vocabulary.html" xmlns:html="http://www.w3.org/1999/xhtml"> <word> <name>Информация</name> <description>Продукт взаимодействия данных и методов, рассмотренный в <html:strong>контексте</html:strong> этого взаимодействия.</description> </word> <word> <name>Байт</name> <description>Единица измерения количества информации, <html:em>8 бит.</html:em></description> </word> <word> <name>Бит</name> <description>Один разряд двоичного кода (двоичная <html:a html:href="http://example.org/knowledges/ru/цифра">цифра</html:a>).</description> </word> </vocabulary>

Заметьте, что теперь мы разметили наши описания с помощью HTML. Обратите внимание на то, что и атрибуты, которые используются в XHTML, мы также записываем с префиксами. Теперь, если мы будем писать парсер, нам будет легко дифференцировать структурные и визуальные элементы документа. А теперь мы добавим ещё одно пространство имён, но уже к другому элементу, и определим внутреннюю структурную ссылку одного понятия на другое.

<?xml version="1.0" encoding="utf-8" ?> <vocabulary xmlns="http://example.com/namespace/vocabulary.html" xmlns:html="http://www.w3.org/1999/xhtml"> <word> <name>Информация</name> <description> Продукт взаимодействия данных и методов, рассмотренный в <html:strong>контексте</html:strong> этого взаимодействия. </description> </word> <word xmlns:sa="http://examplex.com/namespace/vocabulary-seealso.html" sa:seealso="Бит, Информация"> <name>Байт</name> <description> Единица измерения количества информации, <html:em>8 <sa:link sa:word="Бит">бит</sa:link>.</html:em> </description> </word> <word> <name>Бит</name> <description> Один разряд двоичного кода (двоичная <html:a html:href="http://example.org/knowledges/ru/цифра">цифра</html:a>). </description> </word> </vocabulary>

Теперь при обработке документа и реализации соответствующего алгоритма, мы можем создавать ссылки с одного слова на другое, или объединять слова в целые смысловые группы.

XHTML и пространство имён

Web-разработчики, использующие XHTML, могли видеть этот загадочный атрибут в самом начале документа. Возьмём для изучения первые две строчки текущего документа:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html lang="ru" xml:lang="ru" xmlns="http://www.w3.org/1999/xhtml">

Первая строка — это определение типа документа, а вторая — это корневой элемент этого же документа. Корневой элемент содержит искомое определение пространства имён. Здесь он написан в безпрефиксной форме, то есть все элементы документа — это XHTML-элементы (это понятие раскрывается теперь просто чудесно: XHTML-элементы — это элементы, которые определены в соответствующем пространстве имён XHTML. Девушки будут в восторге!). При использовании сторонних элементов или других пространств имён, валидатор должен выдать ошибку. В первом случае он сделает это, потому что набор используемых элементов определён в DTD, привязанном к документу. Во втором случае это произойдёт, потому что других пространств имён не определено.

Заметьте, что в XHTML определены два атрибута из пространства имён xml, один из которых вы можете видеть в примере выше — xml:lang. Не удивляйтесь: если мы определили пространство имён по умолчанию для данного элемента и всех его потомков, то пространство имён xmlns:xml никуда не делось! Записывают данные атрибуты с префиксом для того, чтобы удовлетворить требованиям применения пространств имён и чтобы не было конфликтов с определением одинаковых по именам атрибутов в разных пространствах имён. Второй подобный атрибут в XHTML вы можете обнаружить самостоятельно в текстах DTD.

Послесловие

Некоторые очень сильно ругают теорию пространств имён в XML, находя её слишком избыточной и странной. Автор считает, что такое решение — это самое изящное решение, которое вообще можно было придумать для XML и его потомков и братьев, так как оно хорошо зарекомендовало себя и в других областях не только разработки, но и обычной жизни. Как уже было сказано ранее много раз, упорядочение и систематизация никогда не играли против человека, а всегда помогали ему выжить под натиском суровых реалий окружающего хаоса. На этой философской ноте меня вынуждают остановиться.

Если вам захочется технических подробностей об этом явлении, обратитесь к соответствующей рекомендации W3. До новых встреч и удачных вам пространств!

Мнения (11)

Все эти хорошие люди уже прокомментировали запись. Поделитесь собственным мнением, расскажите, что вы думаете о поставленной проблеме, задаче, озвученных мыслях.

  • Arwen

    22 января 2009 г.11:49

    Да, девушки и правда в восторге. Спасибо за такое простое и доходчивое объяснение. Статья читается на одном духу. С почином постописательства в этом году. :)

  • kinder

    22 января 2009 г.12:47

    Все же хотелось, увидеть больше примеров для использования пространств имен для xhtml, а то получился рассказ про фенечку, а как её применить, куда и для чего не ясно… но заинтриговал это да =)

  • Дин автор

    22 января 2009 г.14:33

    @Kinder, рассказывать про пространства имён в XHTML нечего — их запрещено использовать в нём, а на основе XML приведён хоть и небольшой, но вполне ясный пример. XMLNS не такая тяжёлая тема, чтобы разбирать её на одних примерах, правда?

    Хотя, если нужны какие-то конкретные примеры, то можете просить или приводить сами — будем вместе разбираться.

  • Miscђka

    23 января 2009 г.14:29

    Пространствовал по пространствам с удовольствием.

  • Tenshi

    25 января 2009 г.09:37

    в действительности пространства имён — это фигня, ибо: 1. в каждом файле приходится писать расшифровки префиксов, а порой они занимают прилично места и просто напрашиваются на вынос в отдельный файл. 2. нельзя создать вложенные пространства имён

    я бы предпочёл такую схему: в xml-ке идёт нагромождение тэгов с любыми именами, а в отдельной «xml-аннотации» идёт указание эквивалентности между используемыми тут именами и глобальными идентификаторами. например:

    o:user [является эземпляром] http://example.org/common/#person o:user [является эземпляром] http://example.org/common/#account

    при этом person и account — это различные классы, но o:user является экземпляром их обоих. да, это можно описать с помощью rdf+owl, но как-то там всё слишком сложно =( а массовые переименовывания можно делать с помощью паттернов (квадратные скобки вместо угловых):

    [triplet] [subject]xsl:[word]name[/word][/subject] [relation]instanceOf[relation] [object]http://example.org/common/#[var]name[/var][object] [/triplet] такого rdf owl не умеют.

    касательно практических примеров использования простанств имён в хтмл: http://d-o-b.ru/?article:kill.html

  • Дин автор

    25 января 2009 г.15:07

    @Tenshi, у вас получилось гораздо сложнее, чем аналогичное использование XML Namespaces.

    Я не согласен, что последние являются «фигнёй» только из-за того, что они не поддерживают вложенность и заставляют писать префиксы перед именами элементов.

    Вложенность они и не должны поддерживать, так как разрешают лишь проблему смешивания между собой элементов различных классов, то есть здесь нет иерархии элементов и не может быть.

    А вот по поводу массивности — решается современными XML-редакторами, которые поддерживают автодополнение как пространств документа, так и самих имён элементов (или их атрибутов, в расширенном виде, разумеется). Касаемо размера таких XML: XML Binary compression или обычное GZ-сжатие поможет уменьшить размер XML.

    Говоря о процессе документирования XML Namespaces, то если вы читали статью, заметили, что те самые связки с URI в XMLNS и можно использовать для описания тех или иных элементов. Причём, это ещё и говорит о том, что важна правильная группировка элементов, с помощью которой вы получите такое их логическое разделение, что их будет удобно использовать в дальнейшем. Комментирование некоторых узлов документа в случае применения пространств имён позволит в дальнейшем сгенерировать довольно полную документацию по конкретному пространству и его элементам (атрибутам).

    Интересное предложение. Однако, вы представьте, как будет выглядеть парсинг ваших документов, когда в первом из них лежат все имена в разбросе? Вам во втором документе придётся указывать не просто имена элементов и соответствующие им пространства, а использовать хотя бы XPath для выделения пересечений элементов. В таком случае, проще взять какой-нибудь формат-производную от XML, например XML Schema. Но обработка (парсинг) всего этого будет довольно сложна (сложнее, чем аналогичная с XML Namespaces). Превращать XML в X# (если интересно, можете поискать) я бы не стал, так как обычный формат хранения и передачи данных абсолютно не предназначен для таких «страшных» вещей, для которых создавался RDF и OWL.

  • Tenshi

    25 января 2009 г.18:46

    хороший язык должен быть простым и удобным сам по себе, а не требовать использования нетривиальных идей. xmlschema — это механизм валидации. я же говорил о семантике. вот возьмём, например, rss1.0 - там можно использовать rss:title (заголовок фида), а можно dc:title (абстрактный заголовок), а можно и my:album (заголовок альбома). при этом классы rss:title и my:album являются различными, но конкретно вот в данном месте элемент является экземпляром их обоих. единственно решение такого конфликта в совеременном xml — вкладывать тэги друг в друга, но получается, что некоторым клиентам придут чужие вложенные тэги, а другие не смогут найти свои (ибо ищут их на первом уровне вложенности), что может привести к плачевным последствиям. а понимать смысл тэгов должен не только человек, но и робот. чтобы знать, например, что выводить в заголовок окна при отображении нашей xml-ки.

  • Дин автор

    25 января 2009 г.18:57

    @Tenshi, Ваше мнение, в принципе, понятно. Спасибо, что поделились.

  • Tenshi

    25 января 2009 г.18:59

    или другой пример — форматы rss1.0, 2.0 и atom по структуре своей квазиидентичны — разница лишь в именах и пространствах имён. почему нужно создавать несколько отдельных потоков в разных форматах, если можно сделать один и в отдельном файле указать, что использованный у нас тут my:album эквивалентен rss1:channel | rss2:channel | atom:feed

  • SelenIT

    28 января 2009 г.18:18

    Спасибо, давно хотел найти что-нибудь подобное, популярное (наверное, я немножко девушка в душе:). Но почему-то эти загадочные штуковины — неймспейсы которые — остались для меня не менее загадочными, чем раньше. Никак не могу взять в толк, где хранятся сами списки имен для того или иного пространства. По ссылке на DTD хотя бы открывается реальный документ, который можно «оттрепанировать» (кстати, еще раз спасибо за то объяснение!), а что реально соответстветствует URI неймспейса? Или я вообще фатально не ухватываю суть?

  • Дин автор

    28 января 2009 г.19:51

    @SelenIT, стандарт определения формата описания элементов и атрибута пространств имён никем не задан и не стандартизирован. Здесь ситуация несколько иная, нежели чем с DTD.

    Сами списки элементов нигде не хранятся. Тот URI, который указывается в качестве значения атрибута xmlns (или xmlns:), он, всего-лишь, уникальный идентификатор этого самого пространства имён. Остальное остаётся за разработчиком, который создал это пространство: либо описать конкретно каждый элемент и каждый атрибут, который он использовал в документе в рамках конкретного пространства имён, либо не делать ничего.

    Станет намного лучше, если в будущем появится формат описания конкретных элементов. Пока же разные разработчики по-разному применяют этот формат.

    Сам XML Namespace, к примеру, определён достаточно порядочно: описано его назначение, некоторые атрибуты и даны полезные (связанные с пространством) ссылки.

    Microsoft пошла по правильному, считаю, пути: она на странице к определённому пространству имён даёт ссылки на XML Schema, которая соответствует документу. В ней-то и описаны все элементы и атрибуты со всевозможными пространствами имён. В качестве примера подойдёт Microsoft Office Project 2007 XML Data Interchange.

Я тоже знаю!

Для обращения к человеку используйте символ @, после которого следует имя того, к кому обращаетесь (пробелы заменяются на знак подчёркивания). Если вам интересно, можете подписаться на комментарии по RSS или по эл. почте. Ведите себя достойно, вы же не роботы, правда?

Вы можете использовать следующие XHTML-элементы в разметке комментария: strong, em, span[class=crossline], a[href=uri], code[type=язык], blockquote, ul и ol. В качестве языка кода может быть указан, например, javascript или css.