#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 шаблона.