Вопрос

Я пытаюсь выполнить преобразование 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 со своими собственными пространствами имен и схемамиLocations:

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

Структура A и 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>
Это было полезно?

Решение

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

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

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

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

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

<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.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top