Blank xmlns = "" Атрибуты из импорта - PullRequest
4 голосов
/ 30 апреля 2009

Я пытаюсь выполнить преобразование в документе XML. Мое преобразование XML может привести к двум различным типам базовых элементов в зависимости от значения определенного элемента:

<xsl:template match="/">
  <xsl:choose>
    <xsl:when test="/databean/data[@id='pkhfeed']/value/text()='200'">
      <xsl:call-template name="StructureA">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="StructureB">
        <xsl:with-param name="structure" select="//databean" />
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

StructureA или StructureB создаются с их собственными пространствами имен и схемами. Расположение:

<StructureA xmlns="http://...">

StructureA & B совместно используют некоторые общие элементы, поэтому они определены в отдельном файле с именем «xmlcommon.xslt», из которого обе структуры включают шаблоны. Этот файл xmlcommon не имеет пространства имен по умолчанию, определенного, так как я хочу, чтобы его можно было использовать из пространства имен, определенного в StructureA или StructureB. Но когда я запускаю свое преобразование, любые шаблоны, извлеченные из общего файла, приводят к пустым атрибутам xmlns:

<StructureA xmlns="http://...">
  <SharedElement xmlns="">Something</SharedElement>
</StructureA>

При проверке вместо правильного родительского используется пустое пространство имен. Кто-нибудь знает, как я могу запретить моим шаблонам в моем общем файле добавлять эти пустые атрибуты xmlns?

Вот фрагмент из общего файла:

<xsl:stylesheet version="1.0" xmlns:fn="http://www.w3.org/2005/02/xpath-functions" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template name="ControlledListStructure">
    <xsl:param name="xmlElem" />
    <xsl:param name="structure" />

    <xsl:element name="{$xmlElem}">
      <!-- Blah blah blah -->
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

1 Ответ

9 голосов
/ 30 апреля 2009

Главное, что нужно понять, это то, что ваша таблица стилей диктует имя каждого элемента, который вы добавляете в дерево результатов. Имя элемента состоит из двух частей: локального имени и URI пространства имен. В приведенном выше коде вы предоставляете локальное имя (значение $xmlElem), но не указываете URI пространства имен, что означает, что по умолчанию будет использоваться пустая строка. (На самом деле, он принимает пространство имен по умолчанию этого модуля таблицы стилей; поскольку его нет, то это пустая строка.) Другими словами, элемент будет , а не в пространстве имен . При сериализации документа процессор XSLT должен включать в себя xmlns="" un-декларации, чтобы не объявлять пространство имен по умолчанию, отображаемое вверху. В противном случае элемент получит это пространство имен, что не соответствует вашей таблице стилей. Наименее навязчивый способ исправить это - добавить еще один параметр (например, $namespaceURI), так же, как вы используете $xmlElem. Тогда вы бы написали:

<xsl:element name="{$xmlElem}" namespace="{$namespaceURI}">

Теперь результирующий элемент получит любое пространство имен, которое вы указали для него (что приведет к удалению этих объявлений пространства имен по умолчанию).

Это должно ответить на ваш вопрос. В качестве бесплатного бонусного материала я предлагаю следующее. ; -)

Вы должны удалить тест узла text() в своем сравнении значений. Очень редко вам нужно будет напрямую сравнивать значения текстовых узлов. Вместо этого вы можете просто сравнить строковое значение самого элемента (который определяется как объединение строковых значений всех его текстовых узлов-потомков). Это будет выглядеть так:

<xsl:when test="/databean/data[@id='pkhfeed']/value = '200'">

Преимущество такого способа состоит в том, что ваш код не сломается, если там будет скрыт комментарий:

<value>2<!--test-->00</value>

В этом случае есть два текстовых узла («2» и «00»). Ваш первоначальный тест не пройден, так как он проверяет, равен ли какой-либо из них «200». Маловероятно, что в этом случае это произойдет, но в любом случае проверка строкового значения элемента (в отличие от дочерних элементов его текстового узла) является хорошей практикой, когда вы этого намерены.

Наконец, я призываю вас ознакомиться с правилами шаблонов и контекстом XPath. Я стараюсь избегать <xsl:choose>, <xsl:call-template> и <xsl:with-param>, когда это возможно. С одной стороны, шаблонные правила могут помочь вам избежать многих уродливых, многословных частей XSLT.

<xsl:template match="/databean[data[@id='pkhfeed']/value = '200']" priority="1">
  <StructureA xmlns="http://...">
    ...
  </StructureA>
</xsl:template>

<xsl:template match="/databean">
  <StructureB xmlns="http://...">
    ...
  </StructureB>
</xsl:template>

Даже если вы продолжаете использовать <xsl:call-template>, вам не нужно передавать этот параметр $structure, так как текущий узел останется неизменным в вызываемом шаблоне. Вы можете также легко получить доступ к //databean (или /databean, что, как я подозреваю, то, что вы имеете в виду) из любого из ваших шаблонов StructureA или StructureB, поскольку текущий узел все равно будет "/" (документ узел).

Если вы хотите узнать больше о базовой модели обработки XSLT и ее наиболее мощных функциях (правилах шаблонов), тогда я советую вам ознакомиться с «Как работает XSLT» , глава с бесплатным примером из моего XSLT 1.0 Pocket Reference .

Надеюсь, это было полезно для вас, даже если это больше, чем вы рассчитывали!

...