- A View to a DataSet
- Parsing XML with the DataSet
- The Strongly Typed DataSet
- Conclusion
Parsing XML with the DataSet
XML has a native role in the ADO.NET data stack through the DataSet class. The DataSet can parse and generate XML content from data sources that have the appropriate structured content. This provides a nice alternative to the usual set of XML interface APIs, such as the DOM node-based XmlDocument or the stream-based XmlReader.
The DataSetXML form shown in Listing 4 demonstrates how to generate XML from a loaded DataSet. The form display is depicted in the screenshot shown in Figure 5. The LoadDB button loads the Customers and Orders tables into the DataSet and then links them in a nested fashion. The XML output produced in the text box control named MainTextBox comes from calling GetXml on the DataSet.
The SaveXML button can be used to persist the DataSet to data after a load from the database. The WriteXml method of the DataSet handles the hard chores for the task and has the option of several output formats, using the method overload that takes the XmlWriteMode enumeration:
IgnoreSchema produces raw XML data.
WriteSchema includes an embedded XML schema document.
Diffgram produces an XML format suitable for updating SQL Server 2000 table data through its UpdateGram facilities.
The Schema check box on the form toggles between IgnoreSchema and WriteSchema. The Attribute check box determines whether the code loops through the Customers DataTable to set each DataColumn's ColumnMapping enum to Attribute instead of the default Element setting.
Once you have saved the XML content to file, you can then load it using the LoadXML button, which uses the ReadXml method of the DataSet. The button click handler also deciphers the internal structure of the DataSet by calling the ReadStructure to load the StructureListBox control to see how it maps the hierarchical view of the XML document to the relational view of the DataSet.
The mapping task of XML to relational data follows a finite set of rules. Attributes in an XML document are mapped to Column values on the parent DataTable that represents their parent element. Elements that have only text content are mapped to a Column value as well, while elements that are repeated under a parent element or have complex content beneath them get their own table. To link two nested elements, a hidden primary key and foreign key are sometimes added to the parent and child DataTables along with a linking DataRelationship. The ColumnMapping property of these generated key columns is set to Hidden so that the XML serialization process doesn't output them in the result XML document.
The hidden field behavior with our sample is noticeable if you save the XML to file without a schema. In a load using the LoadXML button, a Customer_Id DataColumn is added to both the Customers and Orders tables, with a ColumnMapping property set to Hidden. Try the example by loading from database and saving with schema to remove the need for the hidden column. This allows the DataSet to reuse your regular data as foreign key and primary key, using the unique/keyref constructs of XML schema.
Listing 4DataSet and XML
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; namespace DisconnectedOpsIIDemo { /// <summary> /// Summary description for DataSetXML. /// </summary> public class DataSetXML : System.Windows.Forms.Form { [STAThread] static void Main(string[] args) { Application.Run(new DataSetXML()); } private DataSet CustDS; private System.Windows.Forms.DataGrid MainDataGrid; private System.Windows.Forms.Button btnLoadDB; private System.Windows.Forms.Button btnLoadXML; private System.Windows.Forms.OpenFileDialog openFileDialog1; private System.Windows.Forms.Button btnSaveXML; private System.Windows.Forms.SaveFileDialog saveFileDialog1; private System.Windows.Forms.CheckBox chkAttributes; private System.Windows.Forms.CheckBox chkEmbedSchema; private System.Windows.Forms.ListBox StructureListBox; private System.Windows.Forms.TextBox MainTextBox; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public DataSetXML() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.MainDataGrid = new System.Windows.Forms.DataGrid(); this.btnLoadDB = new System.Windows.Forms.Button(); this.btnSaveXML = new System.Windows.Forms.Button(); this.btnLoadXML = new System.Windows.Forms.Button(); this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); this.chkAttributes = new System.Windows.Forms.CheckBox(); this.chkEmbedSchema = new System.Windows.Forms.CheckBox(); this.StructureListBox = new System.Windows.Forms.ListBox(); this.MainTextBox = new System.Windows.Forms.TextBox(); ((System.ComponentModel.ISupportInitialize) (this.MainDataGrid)).BeginInit(); this.SuspendLayout(); // // MainDataGrid // this.MainDataGrid.DataMember = ""; this.MainDataGrid.HeaderForeColor = System.Drawing.SystemColors.ControlText; this.MainDataGrid.Location = new System.Drawing.Point(8, 24); this.MainDataGrid.Name = "MainDataGrid"; this.MainDataGrid.Size = new System.Drawing.Size(608, 112); this.MainDataGrid.TabIndex = 14; // // btnLoadDB // this.btnLoadDB.Location = new System.Drawing.Point(8, 320); this.btnLoadDB.Name = "btnLoadDB"; this.btnLoadDB.TabIndex = 15; this.btnLoadDB.Text = "Load DB"; this.btnLoadDB.Click += new System.EventHandler( this.btnLoadDB_Click); // // btnSaveXML // this.btnSaveXML.Location = new System.Drawing.Point(464, 312); this.btnSaveXML.Name = "btnSaveXML"; this.btnSaveXML.TabIndex = 16; this.btnSaveXML.Text = "Save XML"; this.btnSaveXML.Click += new System.EventHandler( this.btnSaveXML_Click); // // btnLoadXML // this.btnLoadXML.Location = new System.Drawing.Point(104, 320); this.btnLoadXML.Name = "btnLoadXML"; this.btnLoadXML.TabIndex = 17; this.btnLoadXML.Text = "Load XML"; this.btnLoadXML.Click += new System.EventHandler( this.btnLoadXML_Click); // // saveFileDialog1 // this.saveFileDialog1.FileName = "doc1"; // // chkAttributes // this.chkAttributes.Location = new System.Drawing.Point(200, 312); this.chkAttributes.Name = "chkAttributes"; this.chkAttributes.Size = new System.Drawing.Size(104, 32); this.chkAttributes.TabIndex = 19; this.chkAttributes.Text = "Save using XML attributes"; // // chkEmbedSchema // this.chkEmbedSchema.Location = new System.Drawing.Point(312, 312); this.chkEmbedSchema.Name = "chkEmbedSchema"; this.chkEmbedSchema.Size = new System.Drawing.Size(144, 32); this.chkEmbedSchema.TabIndex = 20; this.chkEmbedSchema.Text = "Save with embedded XML schema"; // // StructureListBox // this.StructureListBox.HorizontalScrollbar = true; this.StructureListBox.Location = new System.Drawing.Point(368, 152); this.StructureListBox.Name = "StructureListBox"; this.StructureListBox.ScrollAlwaysVisible = true; this.StructureListBox.Size = new System.Drawing.Size(248, 147); this.StructureListBox.TabIndex = 21; // // MainTextBox // this.MainTextBox.Location = new System.Drawing.Point(8, 144); this.MainTextBox.Multiline = true; this.MainTextBox.Name = "MainTextBox"; this.MainTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; this.MainTextBox.Size = new System.Drawing.Size(344, 160); this.MainTextBox.TabIndex = 22; this.MainTextBox.Text = ""; this.MainTextBox.WordWrap = false; // // DataSetXML // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(624, 349); this.Controls.AddRange( new System.Windows.Forms.Control[] { this.MainTextBox, this.StructureListBox, this.chkEmbedSchema, this.chkAttributes, this.btnLoadXML, this.btnSaveXML, this.btnLoadDB, this.MainDataGrid}); this.Name = "DataSetXML"; this.Text = "DataSetXML"; ((System.ComponentModel.ISupportInitialize) (this.MainDataGrid)).EndInit(); this.ResumeLayout(false); } #endregion private void ReadStructure(DataSet ds) { StructureListBox.Items.Clear(); foreach (DataTable dt in ds.Tables) { StructureListBox.Items.Add("Table:" + dt.TableName); foreach(DataColumn dc in dt.Columns) { StructureListBox.Items.Add( "Col:" + dc.ColumnName + " Mapping:" + dc.ColumnMapping.ToString()); } } foreach (DataRelation dr in ds.Relations) { StructureListBox.Items.Add( "Relation:" + dr.RelationName); StructureListBox.Items.Add( "ParentTable: " + dr.ParentTable.TableName); foreach (DataColumn dc in dr.ParentColumns) { StructureListBox.Items.Add( "Col:" + dc.ColumnName); } StructureListBox.Items.Add( "ChildTable: " + dr.ChildTable.TableName); foreach (DataColumn dc in dr.ChildColumns) { StructureListBox.Items.Add( "Col:" + dc.ColumnName); } } } private void btnLoadDB_Click(object sender, System.EventArgs e) { MainDataGrid.DataSource = null; CustDS = new DataSet("CustOrdersDS"); DataAdapterUtility.LoadCustomers(CustDS); DataAdapterUtility.LoadOrders(CustDS); DataAdapterUtility.LinkCustOrders(CustDS); MainDataGrid.DataSource = C ustDS.Tables["Customers"].DefaultView; MainTextBox.Text = CustDS.GetXml(); ReadStructure(CustDS); } private void btnLoadXML_Click(object sender, System.EventArgs e) { DialogResult result = openFileDialog1.ShowDialog(this); if (result == DialogResult.OK) { MainDataGrid.DataSource = null; CustDS = new DataSet(); CustDS.ReadXml(openFileDialog1.FileName); MainDataGrid.DataSource = CustDS; MainTextBox.Text = CustDS.GetXml(); ReadStructure(CustDS); } } private void btnSaveXML_Click(object sender, System.EventArgs e) { DialogResult result = saveFileDialog1.ShowDialog(this); if (result == DialogResult.OK) { foreach (DataColumn dc in CustDS.Tables["Customers"].Columns) { if (chkAttributes.Checked == true) dc.ColumnMapping = MappingType.Attribute; else dc.ColumnMapping = MappingType.Element; } if (chkEmbedSchema.Checked == true) { CustDS.WriteXml( saveFileDialog1.FileName, XmlWriteMode.WriteSchema); MainTextBox.Text = CustDS.GetXml(); MainTextBox.Text += CustDS.GetXmlSchema(); ReadStructure(CustDS); } else { CustDS.WriteXml(saveFileDialog1.FileName); MainTextBox.Text = CustDS.GetXml(); ReadStructure(CustDS); } } } } }
Figure 5 Generating and parsing XML with the DataSet.