Click here to Skip to main content
Click here to Skip to main content
Go to top

Convert XML to Strongly Typed DataSets

, 3 Dec 2003
Rate this:
Please Sign up or sign in to vote.
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 netnicks1. 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.

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:

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

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

OR

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:

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

Share

About the Author

Paperless
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 Pinmemberwrlucas1-Nov-12 3:59 
GeneralMy vote of 3 Pinmemberrajhans14320-Oct-10 18:55 
GeneralTYPO: Missing closing brace before the method: 'public void Fill(DataSet ds)' PinmemberAntho7122-Jan-09 20:46 
Questionselect Generate DataSet from the Schema menu [modified] PinmemberMyronJames18-Jul-07 4:07 
AnswerRe: select Generate DataSet from the Schema menu Pinmemberzheidepriem18-Jul-07 11:04 
GeneralSystem.IO.MemoryStream PinmemberFredParcells7-Jun-07 6:07 
Generalfoo.ReadXML() works fine with second parameter as XMLReadMode.ReadSchema PinmemberGuenaVan19-Feb-06 11:20 
GeneralCustom generator tool PinmemberSasa Popovic23-Nov-05 4:18 
GeneralRe: Custom generator tool PinmemberEsam Salah11-Sep-07 7:00 
GeneralStatic method instead of inheritance Pinmembergyxi5-Sep-05 0:54 
GeneralWhy not just use the xsd.exe ... PinsussUseTheUtilities25-May-05 13:15 
General: error C2504: 'Roster' : base class undefined PinmemberYNOT2617-Jun-04 21:27 
GeneralUse TableName instead od index Pinmembergnejsen13-Jan-04 23:33 
GeneralRe: Use TableName instead od index PinmemberPaperless14-Jan-04 15:42 
GeneralI have tried to use the code but ... Pinmemberdldldl12-Jan-04 3:06 
GeneralUsing with data grids Pinsussrajibaba3-Dec-03 10:29 
GeneralTyped DataSets are good, but... PinmemberPeter Gummer2-Dec-03 18:42 
GeneralRe: Typed DataSets are good, but... PinmemberPaperless3-Dec-03 2:38 
GeneralRe: Typed DataSets are good, but... PinmemberRocky Moore3-Dec-03 15:01 
GeneralRe: Typed DataSets are good, but... PinmemberGovinda3-Dec-03 16:33 
GeneralRe: Typed DataSets are good, but... PinmemberPaperless4-Dec-03 3:40 
GeneralUnclear PinmemberStephen Woods2-Dec-03 16:12 
GeneralRe: Unclear PinmemberPaperless3-Dec-03 3:04 
GeneralReformat please PinmemberJörgen Sigvardsson25-Nov-03 11:21 
GeneralRe: Reformat please Pinmembertbaskan1-Dec-03 23:48 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 4 Dec 2003
Article Copyright 2003 by Paperless
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid