Это преобразование:
<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: то же преобразование, что и в Update1, но написанное на 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>
Обновление: в 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="'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>
ОБНОВЛЕНИЕ 2: в соответствии с запросом ОП, теперь мы реализуем возможность передачи списка имен, разделенных вертикальной чертой, для удаления элементов с этими именами и, соответственно, списка имен, разделенных вертикальной чертой, для удаления атрибуты с этими именами:
<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>
person
Dimitre Novatchev
schedule
21.02.2012