Generating XML
Now that we have explored parsing XML data, we will discuss how to create DataSet content and generate XML output. The same heuristics apply for generate XML as they do for parsing it with the DataSet. Create a table for each element, a column for each attribute or element with simple textual content, and a column for the foreign-key relationship between the tables. Listing 4 is the code for build a DataSet that mirrors the PO.xml document.
Listing 4: DataSet XML Generation
using System; using System.Data; namespace XmlDotNet { public class DataSetCreateDemo { public static void Main() { // call a routine to build the PO structure DataSet ds = BuildPO(); // link the tables in the PO BuildTableRelationships(ds); // add the data AddData(ds); // print out the XML schema document Console.WriteLine("PO schema document"); Console.WriteLine("-----------------------------------------"); Console.WriteLine(ds.GetXmlSchema()); Console.WriteLine(); // print out the XML instance document Console.WriteLine("PO instance document"); Console.WriteLine("-----------------------------------------"); Console.WriteLine(ds.GetXml()); } public static DataSet BuildPO() { // create the DataSet DataSet ds = new DataSet("PoDS"); // Create the Purchase Order element // as a DataTable DataTable po = BuildPOTable(); ds.Tables.Add(po); // Create the BillToAddress element // as a DataTable DataTable billto = BuildAddressTable("BillToAddress"); ds.Tables.Add(billto); // Create the ShipToAddress element // as a DataTable DataTable shipto = BuildAddressTable("ShipToAddress"); ds.Tables.Add(shipto); // Create the LineItem elements // as a DataTable DataTable item = BuildLineItemTable(); ds.Tables.Add(item); return ds; } public static DataTable BuildPOTable() { // Create the PO element // as a DataTable DataTable po = new DataTable("PurchaseOrder"); // add the attributes to the PO element through // DataColumns po.Columns.Add("Number",typeof(string)); po.Columns.Add("OrderDate",typeof(string)); // add the hidden (from XML output) foreign key column DataColumn col = po.Columns.Add("PurchaseOrder_ID",typeof(string)); col.ColumnMapping = MappingType.Hidden; return po; } public static DataTable BuildAddressTable(string name) { // Create the Address element // as a DataTable DataTable addr = new DataTable(name); addr.Columns.Add("Street",typeof(string)); addr.Columns.Add("City",typeof(string)); addr.Columns.Add("State",typeof(string)); addr.Columns.Add("ZipCode",typeof(string)); // add the hidden (from XML output) foreign key column DataColumn col = addr.Columns.Add("PurchaseOrder_ID",typeof(string)); col.ColumnMapping = MappingType.Hidden; return addr; } public static DataTable BuildLineItemTable() { // Create the LineItem element // as a DataTable DataTable item = new DataTable("LineItem"); // add each column with a mapping to XML attribute display DataColumn col = item.Columns.Add("Name",typeof(string)); col.ColumnMapping = MappingType.Attribute; col = item.Columns.Add("Description",typeof(string)); col.ColumnMapping = MappingType.Attribute; col = item.Columns.Add("SKU",typeof(string)); col.ColumnMapping = MappingType.Attribute; col = item.Columns.Add("Price",typeof(decimal)); col.ColumnMapping = MappingType.Attribute; col = item.Columns.Add("Qty",typeof(int)); col.ColumnMapping = MappingType.Attribute; // add the hidden (from XML output) foreign key column col = item.Columns.Add("PurchaseOrder_ID",typeof(string)); col.ColumnMapping = MappingType.Hidden; return item; } public static void BuildTableRelationships(DataSet ds) { // create relationship between BillToAddress element // and PurchaseOrder element, and make it nested elements // in XML output DataRelation dr = new DataRelation("POBillTo", ds.Tables["PurchaseOrder"].Columns["PurchaseOrder_ID"], ds.Tables["BillToAddress"].Columns["PurchaseOrder_ID"], true); dr.Nested = true; ds.Relations.Add(dr); // create relationship between ShipToAddress element // and PurchaseOrder element, and make it nested elements // in XML output dr = new DataRelation("POShipTo", ds.Tables["PurchaseOrder"].Columns["PurchaseOrder_ID"], ds.Tables["ShipToAddress"].Columns["PurchaseOrder_ID"], true); dr.Nested = true; ds.Relations.Add(dr); // create relationship between LineItem elements // and PurchaseOrder element, and make it nested elements // in XML output dr = new DataRelation("POLineItem", ds.Tables["PurchaseOrder"].Columns["PurchaseOrder_ID"], ds.Tables["LineItem"].Columns["PurchaseOrder_ID"], true); dr.Nested = true; ds.Relations.Add(dr); } public static void AddData(DataSet ds) { // add root PurchaseOrder element DataTable po = ds.Tables["PurchaseOrder"]; po.Rows.Add(new object[] {"1001","1/2/01", "0"}); // add BillToAddress element DataTable billto = ds.Tables["BillToAddress"]; billto.Rows.Add(new object[] {"101 Main Street", "Charlotte","NC","28273","0"}); // add ShipToAddress element DataTable shipto = ds.Tables["ShipToAddress"]; shipto.Rows.Add(new object[] {"101 Main Street", "Charlotte","NC","28273","0"}); // add LineItem elements DataTable item = ds.Tables["LineItem"]; item.Rows.Add(new object[] {"Computer Desk", "Wood desk for computer","12345A123", "499.99", "1","0"}); item.Rows.Add(new object[] {"Monitor", "Computer Monitor 19-Inch","19A123", "299.99","1","0"}); item.Rows.Add(new object[] {"Computer", "Pentium 4 Computer","411111","999.99", "1","0"}); item.Rows.Add(new object[] {"Mouse", "Computer Mouse","233B1","49.99","1","0"}); } } }
The content generation begins with building the DataTable and DataColumn structure for each of the elements in the purchase order documents. Creating columns is fairly simple if you want to accept the default mapping to an XML element with textual content represented by the ColumnMapping property of the DataColumn set to Element. For columns that you want to display as XML attributes, change the ColumnMapping property of the DataColumn to Attribute. For the key columns that you create to link tables, use the Hidden enum for the ColumnMapping so that no XML output is generated. After you define the tables, link them using the DataRelation object. Pass in the parent and child columns and pass true as the last parameter to create the foreign key and unique constraints in the DataTable. Also set the Nested property to true so that the XML child elements are displayed underneath their parents.
The final step is to add in the data. Notice the aggregated load used to fill in an object array and pass it directly in the row. After you have created your DataSet, display the XML by calling for an XML string or its schema and its data contents with the GetXml and GetXmlSchema calls. (See Listing 5.)
Listing 5: DataSet XML-Generation Output
PO schema document -------------------------------------------- <?xml version="1.0" encoding="utf-16"?> <xs:schema id="PoDS" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns: msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="PoDS" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="PurchaseOrder"> <xs:complexType> <xs:sequence> <xs:element name="Number" type="xs:string" minOccurs="0" msdata:Ordinal="0" /> <xs:element name="OrderDate" type="xs:string"[ccc] minOccurs="0"[ccc] msdata:Ordinal="1" /> <xs:element name="BillToAddress" minOccurs="0"[ccc] maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="Street" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="0" /> <xs:element name="City" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="1" /> <xs:element name="State" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="2" /> <xs:element name="ZipCode" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="3" /> </xs:sequence> <xs:attribute name="PurchaseOrder_ID"[ccc] type="xs:string" use="prohibited" /> </xs:complexType> </xs:element> <xs:element name="ShipToAddress" minOccurs="0"[ccc] maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="Street" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="0" /> <xs:element name="City" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="1" /> <xs:element name="State" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="2" /> <xs:element name="ZipCode" type="xs:string"[ccc] minOccurs="0" msdata:Ordinal="3" /> </xs:sequence> <xs:attribute name="PurchaseOrder_ID"[ccc] type="xs:string" use="prohibited" /> </xs:complexType> </xs:element> <xs:element name="LineItem" minOccurs="0"[ccc] maxOccurs="unbounded"> <xs:complexType> <xs:attribute name="Name" type="xs:string" /> <xs:attribute name="Description" type="xs:string" /> <xs:attribute name="SKU" type="xs:string" /> <xs:attribute name="Price" type="xs:decimal" /> <xs:attribute name="Qty" type="xs:int" /> <xs:attribute name="PurchaseOrder_ID"[ccc] type="xs:string" use="prohibited" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="PurchaseOrder_ID"[ccc] type="xs:string" use="prohibited" /> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> <xs:unique name="Constraint1"> <xs:selector xpath=".//PurchaseOrder" /> <xs:field xpath="@PurchaseOrder_ID" /> </xs:unique> <xs:keyref name="POLineItem" refer="Constraint1"[ccc] msdata:IsNested="true"> <xs:selector xpath=".//LineItem" /> <xs:field xpath="@PurchaseOrder_ID" /> </xs:keyref> <xs:keyref name="POShipTo" refer="Constraint1"[ccc] msdata:IsNested="true"> <xs:selector xpath=".//ShipToAddress" /> <xs:field xpath="@PurchaseOrder_ID" /> </xs:keyref> <xs:keyref name="POBillTo" refer="Constraint1"[ccc] msdata:IsNested="true"> <xs:selector xpath=".//BillToAddress" /> <xs:field xpath="@PurchaseOrder_ID" /> </xs:keyref> </xs:element> </xs:schema> PO instance document -------------------------------------------- <PoDS> <PurchaseOrder> <Number>1001</Number> <OrderDate>1/2/01</OrderDate> <BillToAddress> <Street>101 Main Street</Street> <City>Charlotte</City> <State>NC</State> <ZipCode>28273</ZipCode> </BillToAddress> <ShipToAddress> <Street>101 Main Street</Street> <City>Charlotte</City> <State>NC</State> <ZipCode>28273</ZipCode> </ShipToAddress> <LineItem Name="Computer Desk"[ccc] Description="Wood desk for computer"[ccc] SKU="123 45A123" Price="499.99" Qty="1" /> <LineItem Name="Monitor"[ccc] Description="Computer Monitor 19-Inch"[ccc] SKU="19A123" Price="299.99" Qty="1" /> <LineItem Name="Computer"[ccc] Description="Pentium 4 Computer"[ccc] SKU="411111" Price="999.99" Qty="1" /> <LineItem Name="Mouse"[ccc] Description="Computer Mouse"[ccc] SKU="233B1" Price="49.99" Qty="1" /> </PurchaseOrder> </PoDS>