• Техногрет
  • Про 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