Question

I got everything working (thank empo) except the ctrlname column. I don't know the syntax well enough. What I am trying to do is use the xslt to sort the xml in the gridview by the column name. Everything is working but the ctrlname column. How do I pass an attribute to the XSLT? I've tried: @name, Data/@name, Data[@name], ctrlname. Nothing works.

    XmlDataSource1.EnableCaching = False
    Dim xslTrnsform As System.Xml.Xsl.XsltArgumentList = New System.Xml.Xsl.XsltArgumentList
    xslTrnsform.AddParam("sortby", "", sortAttr)
    xslTrnsform.AddParam("orderas", "", orderby)
    XmlDataSource1.TransformArgumentList = xslTrnsform
    XmlDataSource1.DataFile = "~/App_LocalResources/DST_Test.xml"
    XmlDataSource1.XPath = "//data"
    XmlDataSource1.TransformFile = xsltFileName
    'XmlDataSource1.DataBind()
    GridView1.DataSource = XmlDataSource1
    GridView1.DataBind()

XSL

<?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]" data-type="text" order="{$orderas}"/>
            </xsl:apply-templates>
        </root>
    </xsl:template>
    <xsl:template match="data">
        <data>
            <xsl:attribute name="ctrlname">
                <xsl:value-of select="@name"/>
            </xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="value" />
            </xsl:attribute>
            <xsl:attribute name="comment">
                <xsl:value-of select="comment" />
            </xsl:attribute>
        </data>
    </xsl:template>
</xsl:stylesheet>

XML input

<?xml version="1.0" encoding="utf-8" ?>
<root>
    <data name="Test1.Text" xml:space="preserve">
        <value>Please Pick Bare Pump</value>
        <comment>Tab - Pump Configuration</comment>
    </data>
    <data name="Test2.Text" xml:space="preserve">
        <value>Complete</value>
        <comment>A07</comment>
    </data>
    <data name="Test3.Text" xml:space="preserve">
        <value>Confirmed</value>
        <comment>A01</comment>
    </data>
</root>
Was it helpful?

Solution

Yes, I'm sorry didnt notice that you wanted also sort by attributes. Note also that you have changed the syntax of xsl:param and it's not correct in that way. It's very important that you keep the single quotes inside the double ones. Here is the final template:

<?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" select="'value'"/>
        <xsl:param name="orderas" select="'ascending'"/>

    <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="ctrlname">
                <xsl:value-of select="@name"/>
            </xsl:attribute>
            <xsl:attribute name="value">
                <xsl:value-of select="value" />
            </xsl:attribute>
            <xsl:attribute name="comment">
                <xsl:value-of select="comment" />
            </xsl:attribute>
        </data>
    </xsl:template>
</xsl:stylesheet>

OTHER TIPS

The currently accepted answer has one flaw: Whenever there is an attribute of data with the same name as a child element of data, the sort will always be performed using as keys the values of the attribute. Also, it is too long.

This solution solves the problem (and is shorter) allowing to specify whether the sort should be by attribute-name or by element-name:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="sortby" select="'attrib!name'"/>
    <xsl:param name="orderas" select="'ascending'"/>
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="root">
        <root>
            <xsl:apply-templates select="data">
                <xsl:sort select=
                "*[name()=substring-after($sortby, 'elem!')]
                |
                 @*[name()=substring-after($sortby, 'attrib!')]"
                data-type="text" order="{$orderas}"/>
            </xsl:apply-templates>
        </root>
    </xsl:template>
    <xsl:template match="data">
        <data ctrlname="{@name}" value="{value}"
              comment="{comment}"/>
    </xsl:template>
</xsl:stylesheet>

When applied on this XML document (based on the provided one, but made a little-bit more interesting):

<root>
    <data name="Test3.Text" xml:space="preserve">
        <value>Please Pick Bare Pump</value>
        <comment>Tab - Pump Configuration</comment>
        <name>X</name>
    </data>
    <data name="Test2.Text" xml:space="preserve">
        <value>Complete</value>
        <comment>A07</comment>
        <name>Z</name>
    </data>
    <data name="Test1.Text" xml:space="preserve">
        <value>Confirmed</value>
        <comment>A01</comment>
        <name>Y</name>
    </data>
</root>

the correct result (sorted by the name attribute) is produced:

<root>
   <data ctrlname="Test1.Text" value="Confirmed" comment="A01"/>
   <data ctrlname="Test2.Text" value="Complete" comment="A07"/>
   <data ctrlname="Test3.Text" value="Please Pick Bare Pump" comment="Tab - Pump Configuration"/>
</root>

Now, replace the <xsl:param name="sortby" select="'attrib!name'"/> with:

<xsl:param name="sortby" select="'elem!name'"/>

and apply the transformation again on the same XML document. This time we get the result correctly sorted by the values of the child-element name:

<root>
   <data ctrlname="Test3.Text" value="Please Pick Bare Pump" comment="Tab - Pump Configuration"/>
   <data ctrlname="Test1.Text" value="Confirmed" comment="A01"/>
   <data ctrlname="Test2.Text" value="Complete" comment="A07"/>
</root>

Explanation:

  1. To distinguish whether we want to sort by an element-child or by an attribute, we use the convention that elem!someName means the sort must be by the values of a child element named someName. Similarly, attrib!someName means the sort must be by the values of an attribute named someName.

  2. The <xsl:sort> insruction is modified accordingly to select as key correctly either an attribute or a child element. No ambiguity is allowed, because the starting substring of the sortby parameter now uniquely identifies whether the key should be an attribute or a child element.

OK, I think this should work for you, allowing you to specify either attribute or element names in the $sortby parameter:

<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>

(you would just pass in "name" as the value of the $sortby parameter)

The problem was that the sort value node selection was only matching elements (* matches elements only).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top