Как фильтровать данные по атрибуту с помощью XSLT?

Как мне получить только те узлы, которые имеют имя, начинающееся с "tag" и заканчивающееся на "DescRes"? Я использую XMLDataSource и GridView (ASP.NET 3.5) для отображения XML. Ничего фантастического. Если значения должны быть жестко закодированы в XSLT, это тоже нормально.

<data name="tagoneCtrlNumberRes.Text" xml:space="preserve">
   <value>1.</value>
   <comment>A01</comment>
</data>
<data name="tagoneCtrlDescRes.Text" xml:space="preserve">
   <value>Hello</value>
   <comment>A01</comment>
</data>
<data name="tagoneCtrlNoteRes.Text" xml:space="preserve">
   <value>Hi</value>
   <comment>A01</comment>
</data>

Если я начну с этого XML, я бы хотел, чтобы результирующий XML после применения XSLT отображал:

<data name="tagoneCtrlDescRes.Text" xml:space="preserve">
   <value>Hello</value>
   <comment>A01</comment>
</data>

Вот мой XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

    <xsl:param name="sortby"></xsl:param>
    <xsl:param name="orderas"></xsl:param>

    <xsl:output method="xml" indent="yes"/>

    <!--<xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>-->
    <xsl:template match="root">
        <root>
            <xsl:apply-templates select="data">
                <xsl:sort select="*[name()=$sortby]|@*[name()=$sortby]" data-type="text" order="{$orderas}"/>
            </xsl:apply-templates>
        </root>
    </xsl:template>
    <xsl:template match="data">
        <data>
            <xsl:attribute name="comment">
                <xsl:value-of select="comment" />
            </xsl:attribute>
            <xsl:attribute name="ctrlname">
                <xsl:value-of select="@name"/>
            </xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="value" />
            </xsl:attribute>            
        </data>
    </xsl:template>
</xsl:stylesheet>

person dotnetN00b    schedule 09.09.2011    source источник
comment
Хороший вопрос, +1. См. мой ответ для однострочного выражения XPath 1.0 - решения и его полной проверки на основе XSLT 1.0. Дается подробное объяснение.   -  person Dimitre Novatchev    schedule 09.09.2011


Ответы (1)


В XSLT 2.0 используйте стандартные функции XPath starts-with()< /a> и ends-with().

В Xpath 1.0 нет функции ends-with(), поэтому вам нужно использовать эквивалентное выражение XPath 1.0.

И. Решение XPath 1.0 / 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:template match="/">
     <xsl:copy-of select=
     "*/data[@name[starts-with(.,'tag')
               and
                 substring(., string-length(.)-11) = 'DescRes.Text'
                 ]
        ]
     "/>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному XML-фрагменту (созданный правильно сформированный XML-документ, заключающий его в один верхний элемент):

<t>
    <data name="tagoneCtrlNumberRes.Text" xml:space="preserve">
        <value>1.</value>
        <comment>A01</comment>
    </data>
    <data name="tagoneCtrlDescRes.Text" xml:space="preserve">
        <value>Hello</value>
        <comment>A01</comment>
    </data>
    <data name="tagoneCtrlNoteRes.Text" xml:space="preserve">
        <value>Hi</value>
        <comment>A01</comment>
    </data>
</t>

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

<data name="tagoneCtrlDescRes.Text" xml:space="preserve">
        <value>Hello</value>
        <comment>A01</comment>
    </data>

Объяснение. Эквивалент ends-with($s, $end) в XPath 1.0:

substring($s, string-length($s) - string-length($end) +1) = $end

II. Решение XPath 2.0/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:template match="/">
     <xsl:sequence select=
     "*/data[@name[starts-with(.,'tag')
               and
                 ends-with(.,'DescRes.Text')
                 ]
        ]
     "/>
 </xsl:template>
</xsl:stylesheet>

Здесь мы используем стандартную функцию Xpath 2.0 ends-with()

person Dimitre Novatchev    schedule 09.09.2011
comment
Только один вопрос. Должен ли он быть именно в таком формате? Я надеялся адаптировать его к тому, что у меня уже есть. Прямо сейчас у меня есть атрибуты, сопоставленные в моем XSLT с моим GridView. - person dotnetN00b; 09.09.2011
comment
@dotnetN00b: ни одно решение не является абсолютным, и большинство решений в SO при решении проблемы определенным образом могут быть повторно использованы/адаптированы в/для класса подобных проблем. В этом конкретном случае вы можете повторно использовать полученные знания в любом сценарии с запросом ends-with(). Конечно, вам нужно иметь хотя бы базовые знания XPath, и это верно для любого языка программирования, на котором вам дают решение. - person Dimitre Novatchev; 09.09.2011
comment
Справедливо. По крайней мере, у меня есть база для работы. Спасибо, Димитр. - person dotnetN00b; 09.09.2011