Изменение одного имени тега в файле XML с помощью XSLT - PullRequest
3 голосов
/ 04 апреля 2010

Могу ли я иметь в XSLT условное выражение, чтобы я нашел и заменил только ПЕРВЫЙ тег с определенным именем тега?

Например, у меня есть XML-файл со многими тегами <title>. Я хотел бы заменить первый из этих тегов на <PageTitle>. Остальные должны быть оставлены в покое. Как бы я сделал это в моем преобразовании? То, что у меня сейчас есть, это:

<xsl:template match="title">
     <PageTitle>
       <xsl:apply-templates />
     </PageTitle>
</xsl:template>

, который находит все теги <title> и заменяет их на <PageTitle>. Любая помощь будет принята с благодарностью!

Ответы [ 3 ]

4 голосов
/ 04 апреля 2010

Первый элемент title в документе выбирается с помощью :

(//title)[1]

Многие ошибочно полагают, что //title[1] выбирает первый title в документе, и это часто совершаемая ошибка. //title[1] выбирает каждый title элемент, который является первым title дочерним элементом его родителя, а не того, что здесь требуется.

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

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match=
  "title[count(.|((//title)[1])) = 1]">

     <PageTitle>
       <xsl:apply-templates />
     </PageTitle>
 </xsl:template>
</xsl:stylesheet>

При применении к этому документу XML :

<t>
 <a>
  <b>
    <title>Page Title</title>
  </b>
 </a>
 <b>
  <title/>
 </b>
 <c>
  <title/>
 </c>
</t>

желаемый результат получен :

<t>
 <a>
  <b>
    <PageTitle>Page Title</PageTitle>
  </b>
 </a>
 <b>
  <title />
 </b>
 <c>
  <title />
 </c>
</t>

Обратите внимание, как мы используем известный метод Кейса пересечения множеств в XPath 1.0 :

Если есть два набора узлов $ns1 и $ns2, следующее выражение выбирает каждый узел, который принадлежит как $ns1, так и $ns2:

$ns1[count(.|$ns2) = count($ns2)]

В конкретном случае, когда оба набора узлов содержат только один узел , и один из них является текущим узлом, следующее выражение оценивается как true() точно, когда два узла идентичны:

count(.|$ns2) = 1

Вариант этого используется в шаблоне совпадения шаблона, который переопределяет правило идентификации:

title[count(.|((//title)[1])) = 1]

соответствует только первому элементу title в документе.

3 голосов
/ 04 апреля 2010

Если все теги заголовков являются братьями и сестрами, вы можете использовать:

<xsl:template match="title[1]">
    <PageTitle>
        <xsl:apply-templates />
    </PageTitle>
</xsl:template> 

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

<xsl:template match="title[not(preceding::title or ancestor::title)]">
    <PageTitle>
        <xsl:apply-templates />
    </PageTitle>
</xsl:template>
3 голосов
/ 04 апреля 2010

Это должно работать:

<xsl:template match="title[1]">
     <PageTitle>
       <xsl:apply-templates />
     </PageTitle>
</xsl:template>

Но оно соответствует первому заголовку в каждом контексте.Таким образом, в следующем примере, оба /a/x/title[1] и /a/title[1] будут сопоставлены.Поэтому вы можете указать что-то вроде match="/a/title[1]".

<a>
    <x>
        <title/> <!-- first title in the context -->
    </x>
    <title/> <!-- first title in the context -->
    <title/>
    <c/>
    <title/>
</a>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...