Как добавить элемент во вложенный список с преобразованием?

#xml #list #xslt

#xml #Список #xslt

Вопрос:

Предположим, у нас есть такой вложенный список в XML:

 <section name="SomeSection">
    <!-- list property -->
    <property length="2" name="ExternalList">
        <!-- first element of external list -->
        <listItem name="ExtElement1">
            <property length="3" name="InternalList1">
                <listItem name="SomeElement1" />
                <listItem name="SomeElement2" />
                <listItem name="SomeElement3" />
            </property>
        </listItem>
        <!-- second element of external list -->
        <listItem name="ExtElement2">
            <property length="2" name="InternalList1">
                <listItem name="SomeElement1" />
                <listItem name="SomeElement2" />
            </property>
        </listItem>
    </property>
</section>
  

И у нас есть какой-то новый элемент, например:

 <listItem name="SomeElement3" />
  

Какое преобразование добавляет ‘SomeElement3’ к ‘InternalList1’ в ‘ExtElement2’, а также
увеличивает атрибут ‘length’ ‘InternalList1’?

Спасибо.

Комментарии:

1. Очевидно, что существует опечатка, которая превращает этот вопрос в бессмыслицу: «ExtElement2» не является потомком «InternalList1» — это его «дядя». Вы, вероятно, имели в виду: добавить ‘SomeElement3’ к ‘ExternalList’ в ‘ExtElement2’, а также увеличить атрибут ‘length’ ‘ExternalList’

2. Хороший вопрос, 1. Смотрите мой ответ для простого, короткого и понятного решения, которое использует один из самых фундаментальных шаблонов проектирования XSLT — переопределение правила идентификации / шаблона. Абсолютно никакие условные инструкции не используются!

Ответ №1:

Это короткое и простое преобразование использует и переопределяет правило идентификации:

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

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

 <xsl:template match=
 "property[@name='ExternalList']
     /listItem[@name='ExtElement2']
       /property[@name='InternalList1']
 ">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
   <xsl:attribute name="length">
    <xsl:value-of select="@length 1"/>
   </xsl:attribute>
   <xsl:apply-templates/>
   <listItem name="SomeElement3" />
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>
  

при применении к предоставленному XML-документу:

 <section name="SomeSection">
    <!-- list property -->
    <property length="2" name="ExternalList">
        <!-- first element of external list -->
        <listItem name="ExtElement1">
            <property length="3" name="InternalList1">
                <listItem name="SomeElement1" />
                <listItem name="SomeElement2" />
                <listItem name="SomeElement3" />
            </property></listItem>
        <!-- second element of external list -->
        <listItem name="ExtElement2">
            <property length="2" name="InternalList1">
                <listItem name="SomeElement1" />
                <listItem name="SomeElement2" />
            </property></listItem>
    </property>
</section>
  

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

 <section name="SomeSection"><!-- list property -->
   <property length="2" name="ExternalList"><!-- first element of external list -->
      <listItem name="ExtElement1">
         <property length="3" name="InternalList1">
            <listItem name="SomeElement1"/>
            <listItem name="SomeElement2"/>
            <listItem name="SomeElement3"/>
         </property>
      </listItem><!-- second element of external list -->
      <listItem name="ExtElement2">
         <property length="3" name="InternalList1">
            <listItem name="SomeElement1"/>
            <listItem name="SomeElement2"/>
            <listItem name="SomeElement3"/>
         </property>
      </listItem>
   </property>
</section>
  

Обратите внимание: В этом решении нигде не используются абсолютно никакие условные инструкции XSLT — они вообще не нужны.

Комментарии:

1. Это более или менее точно такое же, как мое 2-е решение. Я пытался сделать так, чтобы мой первый элемент также не содержал условий, единственная причина, по которой это не так, заключается в том, что match атрибут шаблона должен был бы быть property[@name='ExternalList']/listItem[@name=$extElement] , и все тело шаблона было бы тем, что в данный момент находится в <xsl:when> блоке, но XSLT1 не разрешает переменные / параметры в атрибуте соответствия шаблону.

2. @Flynn1179: Согласен. Но при вашем втором преобразовании вы излишне жестко кодируете neme атрибута «name». Вот почему нам нужно поощрять людей использовать XSLT 2.0.

3. Верно, но в данном случае это показалось адекватным. Тем не менее, я обновил его.

Ответ №2:

Вот простая таблица стилей, которая принимает сведения о новом элементе в качестве параметров:

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

  <xsl:param name="extElement" select="'ExtElement2'" />
  <xsl:param name="intElement" select="'SomeElement3'" />

  <xsl:template match="property[@name='ExternalList']/listItem">
    <xsl:choose>
      <xsl:when test="@name = $extElement">
        <listItem name="{$extElement}">
          <property length="{property/@length 1}" name="{property/@name}">
            <xsl:apply-templates select="property/listItem" />
            <listItem name="{$intElement}" />
          </property>
        </listItem>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="identity" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

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

Или, если вы хотите жестко закодировать его в таблицу стилей, вы можете сделать:

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

  <xsl:template match="listItem[@name='ExtElement2']/property">
    <property length="{@length   1}">
      <xsl:apply-templates select="@*[name() != 'length'] | node()"/>
      <listItem name="SomeElement3" />
    </property>
  </xsl:template>

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

Но вам нужно указать, куда вы хотите, чтобы он попал, в атрибуте match шаблона.