Question

I'm in the process of learning XPath and while practicing w/ an example xml file I found online, the application fails to render any values after implementing an XSL file into the project. Any advice as to what I'm missing or doing wrong is greatly appreciated.

The XML file:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <Customers>
        <Customer>
            <Id>1</Id>
            <FirstName>Joe</FirstName>
            <Surname>Doe</Surname>
        </Customer>
   </Customers>
    <Customers>
          <Customer>
            <Id>2</Id>
            <FirstName>Mary</FirstName>
            <Surname>Brown</Surname>
        </Customer>
    </Customers>
    <Customers>
        <Customer>
            <Id>3</Id>
            <FirstName>Paul</FirstName>
            <Surname>Smith</Surname>
        </Customer>
    </Customers>
    </root>

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:template match ="/">
        <root>
            <xsl:apply-templates select ="//Customers"/>
        </root>
    </xsl:template>
    <xsl:template match ="//Customer">
        <Customer>
            <xsl:attribute name="Id">
                <xsl:value-of select="Id"/>
            </xsl:attribute>
            <xsl:attribute name="FirstName">
                <xsl:value-of select="FirstName"/>
            </xsl:attribute>
            <xsl:attribute name="Surname">
                <xsl:value-of select="Surname"/>
            </xsl:attribute>
        </Customer>
    </xsl:template>
</xsl:stylesheet>

ASPX:

<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
            DataFile="~/xml/Sample-no-attributes.xml" 
            TransformFile="~/xml/Sample-no-attributes.xsl"
            XPath="/root/Customers">
      </asp:XmlDataSource>

        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
            DataSourceID="XmlDataSource1">
            <Columns>
                 <asp:TemplateField HeaderText="CustomerID">
            <ItemTemplate>
                <%# XPath("Customer/Id")%>
             </ItemTemplate>
            </asp:TemplateField>

            <asp:TemplateField HeaderText="First Name">
            <ItemTemplate>
                <%# XPath("Customer/FirstName")%>
             </ItemTemplate>
            </asp:TemplateField>

             <asp:TemplateField HeaderText="Surname">
            <ItemTemplate>
                <%# XPath("Customer/Surname")%>
             </ItemTemplate>
            </asp:TemplateField>
            </Columns>
        </asp:GridView>

Please provide some assistance as to what I'm doing wrong here.

Was it helpful?

Solution

Testing out your XSLT shows the output XML is as follows:

<root>
    <Customer Id="1" FirstName="Joe" Surname="Doe"/>
    <Customer Id="2" FirstName="Mary" Surname="Brown"/>
    <Customer Id="3" FirstName="Paul" Surname="Smith"/>
</root>

If you notice, there are no occurrences of an Customers elements here, yet the XPath expression in your asp:XmlDataSource is "/root/Customers".

Assuming you did want to remove the Customers element, you first need to change your Xpath expression accordingly

XPath="/root/Customer"

But you also have a problem with the XPath expressions in your item templates

    <ItemTemplate>
        <%# XPath("Customer/Id")%>
    </ItemTemplate>

With the current fix, you would need to remove Customer from the expression, because you would now be positioned on a Customer element. But also, in your output XML the Id is now an attribute, not a child element. This means your Xpath would have to be like this (Note the @ symbol to be used for attributes)

    <ItemTemplate>
        <%# XPath("@Id")%>
    </ItemTemplate>

Try this ASPX

 <asp:XmlDataSource ID="XmlDataSource1" runat="server" 
        DataFile="~/Sample-no-attributes.xml" 
        TransformFile="~/Sample-no-attributes.xsl"
        XPath="/root/Customer">
  </asp:XmlDataSource>

    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
        DataSourceID="XmlDataSource1">
        <Columns>
             <asp:TemplateField HeaderText="CustomerID">
        <ItemTemplate>
            <%# XPath("@Id")%>
         </ItemTemplate>
        </asp:TemplateField>

        <asp:TemplateField HeaderText="First Name">
        <ItemTemplate>
            <%# XPath("@FirstName")%>
         </ItemTemplate>
        </asp:TemplateField>

         <asp:TemplateField HeaderText="Surname">
        <ItemTemplate>
            <%# XPath("@Surname")%>
         </ItemTemplate>
        </asp:TemplateField>
        </Columns>
    </asp:GridView>

Note that, if you did want to retain Customers in your XSLT, it might be worth understanding why they went missing. In your first template, you do this

<xsl:apply-templates select ="//Customers"/>

But you do not have a template matching Customers in your XSLT. When this happens, XSLT's built-in templates apply. These output any text nodes within the element, and then look for templates that match its child elements. But it will not output the element itself. Thus you lose the Customers element.

To retain it, you could add the following template

 <xsl:template match ="Customers">
    <Customers>
      <xsl:apply-templates select ="Customer"/>
    </Customers>
 </xsl:template>

However, it would be better to use the XSLT Identity Transform here, to copy existing elements.

Try this XLST, which retains the Customers element. Note how you don't need explicit templates to match Root and Customers. Also note the use of Attribute Value Templates to output the attributes of each Customer element, making the code less verbose.

<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:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match ="//Customer">
    <Customer Id="{Id}" FirstName="{FirstName}" Surname="{Surname}" />
  </xsl:template>
</xsl:stylesheet>

If you did use this XSLT, the XPath of the asp:XmlDataSource would stay as "/root/Customers", but the XPath of the ItemTemplates would be <%# XPath("Customer/@Id")%>

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