Click here to Skip to main content
15,880,543 members
Articles / Programming Languages / C#
Article

Convert XML to Strongly Typed DataSets

Rate me:
Please Sign up or sign in to vote.
4.51/5 (30 votes)
3 Dec 20034 min read 259.1K   79   26
An example of using an XML document to generate a typed DataSet and load it.

Introduction

Looking pretty sharp kiddo, but your DataSet isn’t sharp enough? Don’t you worry netnicks<SUP>1</SUP>. Here is another easy way of sharpening your untyped DataSet into a strongly typed one and making Intellisense out of it.

I’m not going to dwell on the pros and cons of typed vs. untyped DataSets here2. However if your boss or design yells for a strongly typed DataSet and you don’t happen to have a database to conveniently generate it from, perhaps this will ease the pain.

One of the reasons for the hype behind DataSets is their support for XML. Developers can quickly transpose XML from and into a DataSet by using the ReadXml() and WriteXml() methods of the DataSet. However, converting the same XML into a strongly typed DataSet requires some effort. One obvious question is, why can’t we populate the typed DataSet with values from an XML by using the ReadXml method of the auto-generated class foobar, as in: foo.ReadXml(Server.MapPath("roster.xml")), where foo is subclass of foobar. Unfortunately this ReadXml method often causes an Exception: System.IndexOutOfRangeException: There is no row at position 0. Very informative. Not sure if it isn’t implemented or some of the internal indices aren't populated, but the ReadXml fails, and waiting for workarounds from others is always painful [like waiting for the economy or democracy to flourish on an empty stomach]. So for now, we create our own workaround. Besides, there is always the added benefit of learning, right?

Let's consider a common situation in today’s development environment: a web service or another process provides an XML document with dozens or hundreds of elements and a developer facing a task of creating a strongly typed DataSet from that XML. We don’t always have the luxury of using an implicit database connection object automatically to write something like adapter.Fill(theRoster, "ClassRoster") and produce the desired strongly typed DataSet easily. Sorry, no drag and drop convenience of using a table in the Visual Studio XSD designer to help us. So we proceed to implement our own Fill method, by first creating a schema from an XML document.

C#
XmlDocument doc = new XmlDocument();
doc.Load(@"C:\temp\roster.xml");
DataSet ds = new DataSet();byte [] buf = 
  System.Text.ASCIIEncoding.ASCII.GetBytes(doc.OuterXml);
System.IO.MemoryStream ms = new System.IO.MemoryStream(buf);
ds.ReadXml(ms,XmlReadMode.InferSchema);ms.Close();
ds.WriteXmlSchema(@"C:\temp\roster.xsd");

Here is a mini version of a sample input XML document:

XML
<roster>
    <type>demo</type>
    <class>12345</class>
    <instructor>Waldo Erickson</instructor>
    <title>Introduction to .NET</title>
    <student>
        <grade>P</grade>
        <name>Jane Doe</name>
        <phone>555-1212</phone>
    </student>
    <student>
        <grade>F</grade>
        <name>John Hush</name>
        <phone>779-1490</phone>
    </student>
</roster>

and what the resulting roster.xsd schema looks like:

XML
<?xml version="1.0"?>
<xs:schema id="NewDataSet" targetNamespace="http://tempuri.org/roster.xsd"
  xmlns:mstns="http://tempuri.org/roster.xsd"
  xmlns="http://tempuri.org/roster.xsd"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
  attributeFormDefault="qualified"
  elementFormDefault="qualified">

  <xs:element name="roster">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="type" type="xs:string" minOccurs="0" />
        <xs:element name="class" type="xs:string" minOccurs="0" />
        <xs:element name="instructor" type="xs:string" minOccurs="0" />
        <xs:element name="title" type="xs:string" minOccurs="0" />
        <xs:element name="student" minOccurs="0" maxOccurs="unbounded">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="grade" type="xs:string" minOccurs="0" />
              <xs:element name="name" type="xs:string" minOccurs="0" />
              <xs:element name="phone" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="NewDataSet" 
      msdata:IsDataSet="true" msdata:EnforceConstraints="False">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element ref="roster" />
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

Done? Not yet. We created a schema inherited from the XML document, but not a strongly typed DataSet. The schema provides only a “signature” of a typed DataSet - its “body” is another class that represents the schema.

Next, let’s use the roster.xsd file within a Visual Studio project and generate a strongly typed DataSet from it. Start with viewing the roster.xsd file with the Schema tab selected, then select Generate DataSet from the Schema menu. The typed DataSet represented by the newly generated class (roster.cs- not shown here) is empty at this point.

Having generated the XML schema and the typed DataSet from it manually, the next task is to populate it. You mean each row and item one at a time? With data from the same XML document that created it in the first place – what sort of automation is this? Besides, it’s very likely, that data returned from the web service may expand or change and other web services may provide even more complex XML documents in the future. Data always change – one thing you can count on. Can you see the face of a maintenance beast at the end of the tunnel?

Let’s make life easy. What we really want after all this work is a Fill method for the strongly typed DataSet that will work with an input XML document, so we can simply write:

C#
TypedDataSet tds = new TypedDataSet();tds.Fill(xmlDoc);

OR

C#
tds.ReadXml(@"c:\temp\roster.xml");

One solution is to modify the roster.cs class directly and add the Fill method to it. That will pose a problem when we have to regenerate the typed DataSet. We have to add the Fill method again and the practice of modifying a generated class is generally discouraged. Another solution is to inherit from the typed DataSet, and add the Fill method to the new subclass. Thus, when we regenerate the typed DataSet, we won’t loose our newly added Fill method. The class below implements the second solution:

C#
using System;
using System.Data;
using System.Xml; 
namespace Rosters{

public class TypedDataSet: roster      
{
  private TypedDataSet tds;
            
  public void Fill(XmlDocument doc)
  {
    // First populate an untyped DataSet with the xml document
    DataSet ds = new DataSet();
    byte [] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(doc.OuterXml);
    System.IO.MemoryStream ms = new System.IO.MemoryStream(buf);
    ds.ReadXml(ms);
    ms.Close();
    // Then fill the typed DataSet from the untyped DataSet
    Fill(ds);
  }
  
  public new void ReadXml(string xmlFile)
  {
    XmlDocument xmlDoc = new XmlDocument();
    try
    {
      xmlDoc.Load(xmlFile);
      this.Fill(xmlDoc);
    }
    catch (XmlException)
    {
      // ignore or handle exception here
    }
    
    public void Fill(DataSet ds)
    {
      tds = this;
      // Map each row of each table in the untyped DataSet to a
      // corresponding item in the strongly typed DataSet

      for ( int i = 0; i < ds.Tables.Count; i++ )
      {
        foreach (DataRow iDr in ds.Tables[i].Rows)
        {
          DataRow oDr = tds.Tables[i].NewRow();
          for ( int j = 0; j < ds.Tables[i].Columns.Count; j++ )
          try
          {
            oDr[j] = iDr[j].ToString();
          }
          catch (NoNullAllowedException)
          {
            // ignore for now – just in
            // case no such column in DataSet
          }
          tds.Tables[i].Rows.Add(oDr);
        }
      }
    tds.AcceptChanges();
  }
}
}

Points of Interest

This example shows one workaround for the occasional ReadXml method failure of a typed DataSet. The generated roster class in the sample code, inherits from the DataSet class, but gives developers a somewhat more straightforward syntax to get at fields and tables, than it would be with an untyped DataSet2. The public void Fill method above uses a ToString() conversion, because of the inherent nature of XML. Other types could easily be converted, if one chooses to manually assign types other than string, to the elements in the typed DataSet. The outlined conversion approach can be refined and expanded to cover other development tasks. For instance, HttpRequest name-value pairs can be represented in an XML document and subsequently by a typed DataSet, for business object use in another layer.

  1. [netnicks: Members of the .NET generation; true .NET conformists in behavior and attitude].
  2. [For more on the philosophy of typed vs. untyped, see Grady Booch's "Object Oriented Analysis and Design with Applications” and other fine publications.]

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect
United States United States
From the days of punch cards and Assembler code to kayaking on the Web - still having fun.
http://www.kayakonline.com/

Comments and Discussions

 
QuestionIt is 2012 and 4.5 .NetFramework is out Pin
wrlucas1-Nov-12 3:59
wrlucas1-Nov-12 3:59 
GeneralMy vote of 3 Pin
rajhans14320-Oct-10 18:55
rajhans14320-Oct-10 18:55 
GeneralTYPO: Missing closing brace before the method: 'public void Fill(DataSet ds)' Pin
Antho7122-Jan-09 20:46
Antho7122-Jan-09 20:46 
Questionselect Generate DataSet from the Schema menu [modified] Pin
MyronJames18-Jul-07 4:07
MyronJames18-Jul-07 4:07 
AnswerRe: select Generate DataSet from the Schema menu Pin
zheidepriem18-Jul-07 11:04
zheidepriem18-Jul-07 11:04 
GeneralSystem.IO.MemoryStream Pin
FredParcells7-Jun-07 6:07
FredParcells7-Jun-07 6:07 
Generalfoo.ReadXML() works fine with second parameter as XMLReadMode.ReadSchema Pin
GuenaVan19-Feb-06 11:20
GuenaVan19-Feb-06 11:20 
GeneralCustom generator tool Pin
Sasa Popovic23-Nov-05 4:18
Sasa Popovic23-Nov-05 4:18 
GeneralRe: Custom generator tool Pin
Esam Salah11-Sep-07 7:00
Esam Salah11-Sep-07 7:00 
GeneralStatic method instead of inheritance Pin
gyxi5-Sep-05 0:54
gyxi5-Sep-05 0:54 
GeneralWhy not just use the xsd.exe ... Pin
UseTheUtilities25-May-05 13:15
sussUseTheUtilities25-May-05 13:15 
General: error C2504: 'Roster' : base class undefined Pin
YNOT2617-Jun-04 21:27
YNOT2617-Jun-04 21:27 
GeneralUse TableName instead od index Pin
Martin Bring13-Jan-04 23:33
Martin Bring13-Jan-04 23:33 
GeneralRe: Use TableName instead od index Pin
Paperless14-Jan-04 15:42
Paperless14-Jan-04 15:42 
GeneralI have tried to use the code but ... Pin
dldldl12-Jan-04 3:06
dldldl12-Jan-04 3:06 
GeneralUsing with data grids Pin
rajibaba3-Dec-03 10:29
sussrajibaba3-Dec-03 10:29 
Sorry if this is a stupid question, I'm pretty new to DataGrids and DataSets, but when I create a TypedDataSet using the sample xml file and try to bind it to a DataGrid with:

dataGrid1.SetDataBinding(tds, "roster");

the data is not being displayed in the data grid. Any suggestions?
GeneralTyped DataSets are good, but... Pin
Peter Gummer2-Dec-03 18:42
Peter Gummer2-Dec-03 18:42 
GeneralRe: Typed DataSets are good, but... Pin
Paperless3-Dec-03 2:38
Paperless3-Dec-03 2:38 
GeneralRe: Typed DataSets are good, but... Pin
Rocky Moore3-Dec-03 15:01
Rocky Moore3-Dec-03 15:01 
GeneralRe: Typed DataSets are good, but... Pin
Rama.NET3-Dec-03 16:33
Rama.NET3-Dec-03 16:33 
GeneralRe: Typed DataSets are good, but... Pin
Paperless4-Dec-03 3:40
Paperless4-Dec-03 3:40 
GeneralUnclear Pin
Stephen Woods2-Dec-03 16:12
Stephen Woods2-Dec-03 16:12 
GeneralRe: Unclear Pin
Paperless3-Dec-03 3:04
Paperless3-Dec-03 3:04 
GeneralReformat please Pin
Jörgen Sigvardsson25-Nov-03 11:21
Jörgen Sigvardsson25-Nov-03 11:21 
GeneralRe: Reformat please Pin
tbaskan1-Dec-03 23:48
tbaskan1-Dec-03 23:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.