Про xmlns. Часть первая

HTML и CSSXSLTJavaScriptИзображенияСофтEtc
Александр Самиляк

14 июня 2011


Задача.

Рассказать о неймспейсах в XSLT.

Корневым элементом XSL-шаблона является <xsl:stylesheet>, и во многих случаях он выглядит так:

 <xsl:stylesheet
  version="1.0"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  ...
</xsl:stylesheet>

Атрибут version является обязательным, равно как и объявление XSL-неймспейса xmlns:xsl="http://www.w3.org/1999/XSL/Transform" (иначе было бы неясно, где в шаблоне сам XSL-код). А вот зачем нам нужна запись xmlns="http://www.w3.org/1999/xhtml", не очень понятно.


Для начала уясним, что вообще делают эти конструкции, начинающиеся с xmlns. У всесильного W3C на эту тему тоже есть свой документ, озаглавленный «Неймспейсы в XML». Почитав его (перед сном это делать не рекомендуется), мы узнаем, что основной причиной возникновения неймспейсов явилась необходимость отличать XML-элементы, обладающие одним и тем же именем, но имеющие разный смысл и предназначение, относящиеся к разным словарям разметки.

Хорошим примером такого разделения может служить как раз милый нашему сердцу XSL. Скажем, элемент <xsl:text> имеет неймспейс xsl и является управляющим XSL-кодом, тогда как элемент <text> неймспейса не имеет и просто отправляется на вывод, несмотря на то что имя у него тоже text.

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

Форма с префиксом имеет вид:

xmlns : префикс = "полный URI"

Здесь префикс — это некоторое внутреннее имя нашего XML-документа, мы можем использовать любой префикс, какой нам нравится. А вот URI — это такая штука, которая фиксируется раз и навсегда, чтобы при виде этого URI все понимали, какой словарь разметки он представляет. Скажем, написал кто-то на заборе http://www.w3.org/1999/XSL/Transform, и каждому ясно — да это же URI XSL-я! Понятно, что при таком подходе все URI должны быть уникальны.

Следует также понимать, что XML-процессоры не «ходят» в интернет, чтобы по этому адресу чего-то скачать. Это всего лишь уникальный идентификатор. Однако здесь возникает вопрос: а что же он тогда выглядит как адрес в интернете? Почему вместо http://www.w3.org/1999/XSL/Transform не писать, например, «у-вас-ус-отклеился»? Ответ прост: когда-то условились, что по этому адресу URI в интернете должна висеть маленькая страничка, в двух словах рассказывающая, что это за URI и какой цели служит. И страничка эта предназначена для человека, а не для машины.


Итак, объявив неймспейс с префиксом, мы теперь можем его использовать — писать элементы, имеющие этот неймспейс. Как это делать, читатель наверняка знает:

<префикс : имя элемента>

Ровно так все делают в XSL-коде, когда хотят использовать какой-то XSL-элемент, например <xsl:template>. Как я уже сказал, префикс может быть любым, поэтому можно писать и так:

 <god_bless_america:stylesheet
  version="1.0"
  xmlns:god_bless_america="http://www.w3.org/1999/XSL/Transform"
>
  
  <god_bless_america:template match="/">
    ...
  </god_bless_america:template>
  
</god_bless_america:stylesheet>

Но все привыкли использовать xsl — это коротко и удобно.


Переходим к неймспейсу без префикса. Он имеет вид:

xmlns = "полный URI"

Эта конструкция объявляет неймспейс по умолчанию. Он нужен в ситуации, когда при написании элемента мы не указываем префикс, а пишем сразу имя элемента — <div>. То есть элемент, не имеющий префикса, считается принадлежащим к неймспейсу по умолчанию.

А что если неймспейс по умолчанию не объявлен и у элемента нет префикса? Такую ситуацию вэтрицэшники тоже регламентируют: тогда элемент получит неймспейс, не имеющий значения, который называется null.


Следовательно, запись xmlns="http://www.w3.org/1999/xhtml" в начале XSL-шаблона нужна для того, чтобы сообщить XSL-процессору (трансформатору), что все элементы, не имеющие префикса, относятся к этому неймспейсу XHTML-документов. А что это дает? Самое смешное, что ничего особенного.

Все, что произойдет, — это копирование указанного неймспейса в выходной HTML. То есть такой XSL-шаблон:

 <xsl:stylesheet
  version="1.0"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  
  <xsl:template match="/">
    <html>
      <body>
        <p>Дизайн спасет мир</p>
      </body>
    </html>
  </xsl:template>
  
</xsl:stylesheet>

выведет следующее:

 <html xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <p>Дизайн спасет мир</p>
  </body>
</html>

Это, конечно, круто, и этого даже требует XHTML-спецификация, но в жизни от этого ни холодно ни жарко. Другое замеченное изменение в поведении трансформатора: если выставлен <xsl:output method="html" />, то при отсутствии неймспейса по умолчанию выходят незакрытые теги (<br>). Но при xmlns="http://www.w3.org/1999/xhtml" теги начинают закрываться. Однако если кто-то думает, что сайт с валидным HTML работает лучше, чем с невалидным, то ему самое время встать и выйти. Меня это сильно раздражает, ведь сайты делаются для людей, а не для валидатора.

Итак, берусь утверждать, что при выводе HTML ощутимой пользы от этого xmlns="http://www.w3.org/1999/xhtml" нет. А есть ли вред? Оказывается, небольшой есть — от неаккуратного использования.

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

 <html xmlns="http://www.w3.org/1999/xhtml">
  ...
</html>

говорит, что текущий элемент <html> и все его потомки, не имеющие префикса, относятся к неймспейсу http://www.w3.org/1999/xhtml. Это важно. Именно из-за этого в HTML регулярно вылезают эти записи xmlns="http://www.w3.org/1999/xhtml".

Разберемся на примере. Представим, что у нас есть два XSL-шаблона, причем один импортирует другой.

Главный шаблон:

 <xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  
  <xsl:import href="import.xsl" />
  
  <xsl:template match="/">
    <html>
      <head>
        <title>Billie Jean</title>
      </head>
      <body>
        <xsl:call-template name="lyrics" />
      </body>
    </html>
  </xsl:template>
  
</xsl:stylesheet>

Импортируемый шаблон import.xsl:

 <xsl:stylesheet
  version="1.0"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  
  <xsl:template name="lyrics">
    <p>Billie Jean is not my lover</p>
    <p>She's just a girl who claims that I am the one</p>
    <p>But the kid is not my son</p>
  </xsl:template>
  
</xsl:stylesheet>

Результатом выполнения главного шаблона будет:

 <html>
  <head>
    <title>Billie Jean</title>
  </head>
  <body>
    <p xmlns="http://www.w3.org/1999/xhtml">Billie Jean is not my lover</p>
    <p xmlns="http://www.w3.org/1999/xhtml">She's just a girl who claims that I am the one</p>
    <p xmlns="http://www.w3.org/1999/xhtml">But the kid is not my son</p>
  </body>
</html>

Почему посреди нашего HTML вылезли эти xmlns="http://www.w3.org/1999/xhtml", да еще три раза?

Сначала главный шаблон выводит элементы <html>, <head> и <body>. В главном шаблоне нет объявления неймспейса по умолчанию (он null), поэтому никаких xmlns у элемента <html> тоже нет. Но дальше внутри <body> вызывается шаблон name="lyrics", который находится в другом файле import.xsl. И вот у негó уже есть объявление неймспейса по умолчанию, которое действует на все элементы, выводимые в коде этого файла import.xsl. Поэтому при выводе абзацев <p> нам и сообщается, что у них неймспейс http://www.w3.org/1999/xhtml, иначе они примут неймспейс своего родителя <body>, который равен null. Это произошло с каждым абзацем, так как они вышли братьями.


Рассмотрим обратную ситуацию, когда в главном шаблоне есть xmlns="http://www.w3.org/1999/xhtml", а в импортируемом — нет. Тогда на выходе мы получим другой сюрприз:

 <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Billie Jean</title>
  </head>
  <body>
    <p xmlns="">Billie Jean is not my lover</p>
    <p xmlns="">She's just a girl who claims that I am the one</p>
    <p xmlns="">But the kid is not my son</p>
  </body>
</html>

Элемент <html> и все его потомки законно получают XHTML-неймспейс. Но у абзацев-то он null (ибо в их файле import.xsl xmlns не указан), поэтому абзацы бунтуют и говорят нам: «Идите к черту. Не хотим наследовать ваш XHTML. У нас свой неймспейс null». Это выражается в записи xmlns="" у каждого абзаца, которая как раз и означает, что неймспейс этого элемента null.


Вывод: надо или во всех XSL-файлах объявлять неймспейс по умолчанию, или во всех не объявлять. Лично я везде не объявляю — меньше суеты в коде.


В следующей части мы подробнее разберемся в неймспейсах с префиксом.

1
2