jpkeisala
jpkeisala

Reputation: 8916

Gridview binding to XML

I am trying to make a simple grid view that is binded to a simple xml document but I must be missing something since I am keep getting error message:

The data source for GridView with id 'GridView1' did not have any properties or attributes from which to generate columns. Ensure that your data source has content.

Code

<asp:GridView ID="GridView1" runat="server" DataSourceID="XmlDataSource1">
    <Columns>
        <asp:BoundField DataField="id" HeaderText="ID" SortExpression="id" />
    </Columns>
</asp:GridView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
    DataFile="Notifications.xml" XPath="/data/node"></asp:XmlDataSource>

XML

<?xml version="1.0" encoding="utf-8" ?>
<data>
  <node>
    <id>1096</id>
    <name>About Us</name>
    <date>21/12/2009 17:03:43</date>
    <user id="1">writer</user>
  </node>
  <node>
    <id>1099</id>
    <name>News</name>
    <date>21/12/2009 17:03:47</date>
    <user id="1">writer</user>
  </node>
  <node>
    <id>1098</id>
    <name>Another page</name>
    <date>21/12/2009 17:03:52</date>
    <user id="1">writer</user>
  </node>
</data>

Is it perhaps my xpath that is wrong or am I making something fundamentally wrong here?

Upvotes: 4

Views: 25733

Answers (4)

Aaron Hopkins
Aaron Hopkins

Reputation: 1

Dynamic Databind to XML Document

If your Xml is structured with more info, you will be able to iterate over the structure with more ease, as its easier to identify the exact node your looking for.

We have a web service that returns XML in a row/column structure (similar to your data example above)

For speed Ive copy/pasted our solution, but you should get the gist and be able to hack it to do your thing.

<response xmlns="">
  <method name="ExecuteMethod">
  <message>Query Successful</message>
  <summary success="true" rowcount="2" />
  <row>
    <column name="ID"><![CDATA[SomeData]]></column>
    <column name="NHS_NO"><![CDATA[SomeData]]></column>
    <column name="HOSPITALNUMBER"><![CDATA[SomeData]]></column>
    <column name="SURNAME"><![CDATA[SomeData]]></column>
    <column name="FIRST_FORENAME"><![CDATA[SomeData]]></column>
    <column name="TITLE"><![CDATA[SomeData]]></column>
    <column name="SEX"><![CDATA[SomeData]]></column>
    <column name="DOB">SomeData</column>
    <column name="ADDRESS"><![CDATA[SomeData]]></column>
    <column name="POSTCODE"><![CDATA[SomeData]]></column>
    <column name="DOD" />
  </row>
  <row>
    <column name="ID"><![CDATA[SomeData]]></column>
    <column name="NHS_NO"><![CDATA[SomeData]]></column>
    <column name="HOSPITALNUMBER"><![CDATA[SomeData]]></column>
    <column name="SURNAME"><![CDATA[SomeData]]></column>
    <column name="FIRST_FORENAME"><![CDATA[SomeData]]></column>
    <column name="TITLE"><![CDATA[SomeData]]></column>
    <column name="SEX"><![CDATA[SomeData]]></column>
    <column name="DOB">SomeData</column>
    <column name="ADDRESS"><![CDATA[SomeData]]></column>
    <column name="POSTCODE"><![CDATA[SomeData]]></column>
    <column name="DOD" />
  </row>
</method>
</response> 

Here's the c# implementation

  • we get the Column names out to pass to the data in to the Gridviews.Datakey names array
  • we loop over the rows, adding each row to the dataset as we go along
  • we set the gridviews datasounce to the dataset
  • we bind()

There's a bit of css and the control instance for your ease of copy/paste in the example below.

//In Code In Front...

Table.DataGridView{float:left; width:100%;}
Table.DataGridView tr{}
Table.DataGridView th{ background-color:Gray; font-weight:bold; color:White;}
Table.DataGridView td{ background-color:White; color:Black; font-weight:normal;}

<asp:GridView ID="DataGridView" runat="server" CssClass="DataGridView" GridLines="Both" Visible="false" />


//In Code Behind...

XmlNode myXmlNodeObject = myXmlDocService.GetData(_xmlDataString);

    //Bind To GridView

    //Create a DataSet To Bind To
    DataSet ds = new DataSet();
    ds.Tables.Add("XmlDataSet");

    //Get Column Names as String Array
    XmlDocument XMLDoc = new XmlDocument();
    XMLDoc.LoadXml("<result>" +myXmlNodeObject.ChildNodes.Item(0).ChildNodes.Item(2).ParentNode.InnerXml + "</result>"); //Get Row/Columns
    int colCount = myXmlNodeObject.ChildNodes.Item(0).ChildNodes.Item(2).SelectNodes("column").Count;
    string[] ColumnNameArray = new string[colCount];
    int iterator = 0;
    foreach(XmlNode node in myXmlNodeObject.ChildNodes.Item(0).ChildNodes.Item(2).SelectNodes("column"))
    {
        ColumnNameArray.SetValue(node.Attributes["name"].Value ,iterator);
        ds.Tables["XmlDataSet"].Columns.Add(node.Attributes["name"].Value); //Create individual columns in the dataset
        iterator++;
    }

    //Get Data Row By Row to populate the DataSet.Rows
    foreach(XmlNode RowNode in XMLDoc.ChildNodes.Item(0).SelectNodes("row"))
    {
        string[] rowArray = new string[colCount]; 
        int iterator2 = 0;
        foreach(XmlNode ColumnNode in RowNode.ChildNodes)
        {
            rowArray.SetValue(ColumnNode.InnerText, iterator2);
            iterator2++;
        }
        ds.Tables["XmlDataSet"].Rows.Add(rowArray);
    }

    DataGridView.DataSource = ds.Tables["XmlDataSet"];
    DataGridView.DataKeyNames = ColumnNameArray;
    DataGridView.DataBind();
    DataGridView.Visible = true;

Upvotes: 0

Eilon
Eilon

Reputation: 25704

There are a number of ways to get this to work:

  1. Use Brian's solution, which is to rewrite the XML to use attributes instead of sub-nodes.
  2. Use an XSLT transform to dynamically convert the child nodes to attributes. See this SO question for an XSLT that can perform that operation.
  3. Load the XML data into a DataSet, which internally does this conversion.

Here's an example of how to do #3:

DataSet ds = new DataSet();
ds.ReadXml(MapPath("~/App_Data/mydata.xml"));
GridView1.DataSource = ds;
GridView1.DataBind();

The limitation of this last approach is that you don't get automatic databinding as you would with a data source control. However, since the XmlDataSource is a read-only control anyway, that's not necessarily a serious limitation.

Upvotes: 7

Brian Mains
Brian Mains

Reputation: 50728

XmlDataSource works with attributes, not child entities. You need to do:

<node id="1096" name="About Us" ../>

Instead of using child elements. Unfortunately it is this way; I really wish it would work with the alternative; I like that approach much better.

Upvotes: 3

Sean Barlow
Sean Barlow

Reputation: 35

try changeing the xpath to

look like XPath="data/node"

Upvotes: 0

Related Questions