Это преобразование :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="removeElementsNamed" select="'x'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not(name() = $removeElementsNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
при применении к любому документу XML, скажите, что :
<t>
<a>
<b/>
<x/>
</a>
<c/>
<x/>
<d/>
</t>
производиттребуемый правильный результат - копия исходного XML-документа, в котором удаляется любой вхождение элемента, имя которого является значением параметра $removeElementsNamed
, :
<t>
<a>
<b/>
</a>
<c/>
<d/>
</t>
Обратите внимание : В XSLT 1.0 синтаксически недопустимо иметь ссылку на переменную или параметр внутри шаблона совпадения шаблона .Вот почему решения @Jan Thomä и @treeMonkey выдают ошибку с любым XSLT 1.0-совместимым процессором.
Обновление : вот более сложное решение, которое позволяетразделенный список имен элементов - подлежит удалению и передаче в преобразование:
<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:param name="removeElementsNamed" select="'|x|c|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test=
"not(contains($removeElementsNamed,
concat('|',name(),'|' )
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
При применении к тому же XML-документу (см. выше) преобразование снова производит требуемый, правильный вывод -- исходный XML-документ со всеми элементами, чье имя указано в параметре $removeElementsNamed
- удалено :
<t>
<a>
<b/>
</a>
<d/>
</t>
Update2 : то же преобразование, что и в Обновление 1 , но написано в XSLT 2.0:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="removeElementsNamed" select="'|x|c|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"*[name() = tokenize($removeElementsNamed, '\|')]"/>
</xsl:stylesheet>
Обновление : ОП добавил требование также иметь возможность удалять все атрибуты, имеющие определенное имя.
Вот слегка измененное преобразование для соответствия этому новому требованию:
<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:param name="removeElementsNamed" select="'x'"/>
<xsl:param name="removeAttributesNamed" select="'n'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test="not(name() = $removeElementsNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="@*">
<xsl:if test="not(name() = $removeAttributesNamed)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к документу XML ниже (раньше, но с добавлением нескольких атрибутов):
<t>
<a>
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
желаемый, правильный результат получается (все элементы с именем x
и все атрибуты с именем n
удаляются):
<t>
<a>
<b m="1"/>
</a>
<c/>
<d/>
</t>
UPDATE2 : В соответствии с запросом OP, мы теперь реализуем возможность передавать разделенный по каналам список имен для удаления элементов с этими именами и, соответственно, каналасписок имен для удаления атрибутов с этими именами:
<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:param name="removeElementsNamed" select="'|c|x|'"/>
<xsl:param name="removeAttributesNamed" select="'|n|p|'"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:if test=
"not(contains($removeElementsNamed,
concat('|', name(), '|')
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="@*">
<xsl:if test=
"not(contains($removeAttributesNamed,
concat('|', name(), '|')
)
)
">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется к следующему документу XML :
<t>
<a p="0">
<b m="1" n="2"/>
<x/>
</a>
<c/>
<x/>
<d n="3"/>
</t>
желаемый, правильный результат выдается (элементы с именами c
и x
и атрибуты с именами n
и p
удаляются):
<t>
<a>
<b m="1"/>
</a>
<d/>
</t>