Putting Together an XML Document






Putting Together an XML Document

Problem

You have various pieces of a document in XML form that need to be put together to form a single XML documentthe opposite of what was done in Recipe 15.9. In this case, you have received various pieces of an invoice in XML form. For example, one department sent the shipping information as an XML document, one sent the billing information in XML, and another sent invoice line items, also as an XML document. You need a way to put these XML pieces together to form a single XML invoice document.

Solution

In order to reconstitute the original invoice, you need to reverse the process used to create the pieces of the invoice using multiple XmlDocuments. There are three parts being sent back to you to help in re-forming the original invoice XML: BillingEnvelope.xml, ShippingEnvelope.xml, and Fulfillment.xml. These are listed below:


BillingEnvelope XML

	<BillingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
	    <billInfo>
	        <name>Beerly Standing</name>
	        <attn>Accounting</attn>
	        <street>98 North Street</street>
	        <city>Intox</city>
	        <state>NH</state>
	    </billInfo>
	</BillingEnvelope>


ShippingEnvelope XML

	<ShippingEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
	    <shipInfo>
	        <name>Beerly Standing</name>
	        <attn>Receiving</attn>
	        <street>47 South Street</street>
	        <city>Intox</city>
	        <state>NH</state>
	    </shipInfo>
	</ShippingEnvelope>


FulfillmentEnvelope XML

	<FulfillmentEnvelope invoiceDate="2003-10-05" invoiceNumber="INV-01">
	    <item partNum="98745">
	        <productName>Brown Eyed Stout</productName>
	        <quantity>12</quantity>
	        <price>23.99</price>
	        <shipDate>2003-12-20</shipDate>
	    </item>
	    <item partNum="34987">
	        <productName>Diamond Pearl Lager</productName>
	        <quantity>22</quantity>
	        <price>35.98</price>
	        <shipDate>2003-12-20</shipDate>
	    </item>
	    <item partNum="AK254">
	        <productName>Job Site Ale</productName>
	        <quantity>50</quantity>
	        <price>12.56</price>
	        <shipDate>2003-11-12</shipDate>
	    </item>
	</FulfillmentEnvelope>

To put these back together as a single invoice, reverse the process you went through to break it apart, while inferring the invoice date and invoice number from the BillingEnvelope to help reestablish the invoice, as shown in Figure.

Reconstructing an XML document

public static void ReceiveInvoice( )
{ 
    XmlDocument invoice = new XmlDocument( ); 
    XmlDocument billing = new XmlDocument( ); 
    XmlDocument shipping = new XmlDocument( ); 
    XmlDocument fulfillment = new XmlDocument( );

    // Get up root invoice node.
    XmlElement invoiceElement = invoice.CreateElement("Invoice");
    invoice.AppendChild(invoiceElement);

    // Load the billing.
    billing.Load(@"..\..\BillingEnvelope.xml");
    // Get the invoice date attribute.
    XmlAttribute invDate = (XmlAttribute)
      billing.DocumentElement.Attributes.GetNamedItem("invoiceDate");
    // Get the invoice number attribute.
    XmlAttribute invNum = (XmlAttribute)
      billing.DocumentElement.Attributes.GetNamedItem("invoiceNumber");
    // Set up the invoice with this info.
    invoice.DocumentElement.Attributes.SetNamedItem(invDate.Clone( ));
    invoice.DocumentElement.Attributes.SetNamedItem(invNum.Clone( ));
    // Add the billInfo back in.
    XmlNodeList billList = billing.SelectNodes("/BillingEnvelope/billInfo");
    foreach(XmlNode billInfo in billList)
    {
        invoice.DocumentElement.AppendChild(invoice.ImportNode(billInfo,true));
    }

    // Load the shipping.
    shipping.Load(@"..\..\ShippingEnvelope.xml");
    // Add the shipInfo back in.
    XmlNodeList shipList = shipping.SelectNodes("/ShippingEnvelope/shipInfo");
    foreach(XmlNode shipInfo in shipList)
    {
        invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true));
    }

    // Load the items.
    fulfillment.Load(@"..\..\FulfillmentEnvelope.xml");

    // Create an Items element in the Invoice to add these under.
    XmlElement items = invoice.CreateElement("Items");

    // Add the items back in under Items.
    XmlNodeList itemList = fulfillment.SelectNodes("/FulfillmentEnvelope/item");
    foreach(XmlNode item in itemList)
    {
        items.AppendChild(invoice.ImportNode(item,true));
    }

    // Add it in.
    invoice.DocumentElement.AppendChild(items.Clone( ));

    // Display Invoice XML.
    Console.WriteLine("Invoice:\r\n{0}",invoice.OuterXml);

    // Save our reconstitued invoice. 
    invoice.Save(@"..\..\ReceivedInvoice.xml");
}

The code reconstitutes the invoice and saves it as ReceivedInvoice.xml, the contents of which are shown here:

	<Invoice invoiceDate="2003-10-05" invoiceNumber="INV-01"><billInfo><name>Beerly 
	Standing</name><attn>Accounting</attn><street>98 North Street</street><city>Intox</ 
	city><state>NH</state></billInfo><shipInfo><name>Beerly Standing</name><attn> 
	Receiving</attn><street>47 South Street</street><city>Intox</city><state>NH</state></ 
	shipInfo><Items><item partNum="98745"><productName>Brown Eyed Stout</productName> 
	<quantity>12</quantity><price>23.99</price><shipDate>2003-12-20</shipDate></item> 
	<item partNum="34987"><productName>Diamond Pearl Lager</productName><quantity>22</ 
	quantity><price>35.98</price><shipDate>2003-12-20</shipDate></item><item 
	partNum="AK254"><productName>Job Site Ale</productName><quantity>50</quantity><price> 
	12.56</price><shipDate>2003-11-12</shipDate></item></Items></Invoice>

Discussion

In the Solution code, the first step is to create a set of XmlDocuments for the Invoice, BillingEnvelope, ShippingEnvelope, and FulfillmentEnvelope. Then you create the new root Invoice element in the invoice XmlDocument:

	XmlDocument invoice = new XmlDocument( );
	XmlDocument billing = new XmlDocument( );
	XmlDocument shipping = new XmlDocument( );
	XmlDocument fulfillment = new XmlDocument( );

	// Set up root invoice node.
	XmlElement invoiceElement = invoice.CreateElement("Invoice");
	invoice.AppendChild(invoiceElement);

Next, you process the BillingEnvelope, taking the invoice date and number from it and adding it to the Invoice. Then you add the billing information back in to the invoice:

	// Load the billing.
	billing.Load(@"..\..\BillingEnvelope.xml");
	// Get the invoice date attribute.
	XmlAttribute invDate = (XmlAttribute)
	 billing.DocumentElement.Attributes.GetNamedItem("invoiceDate");
	// Get the invoice number attribute.
	XmlAttribute invNum = (XmlAttribute)
	 billing.DocumentElement.Attributes.GetNamedItem("invoiceNumber");
	// Set up the invoice with this info.
	invoice.DocumentElement.Attributes.SetNamedItem(invDate.Clone( ));
	invoice.DocumentElement.Attributes.SetNamedItem(invNum.Clone( ));
	// Add the billInfo back in.
	XmlNodeList billList = billing.SelectNodes("/BillingEnvelope/billInfo");
	foreach(XmlNode billInfo in billList)
	{
	    invoice.DocumentElement.AppendChild(invoice.ImportNode(billInfo,true));
	}

The ShippingEnvelope came next:

	// Load the shipping.
	shipping.Load(@"..\..\ShippingEnvelope.xml");
	// Add the shipInfo back in.
	XmlNodeList shipList = shipping.SelectNodes("/ShippingEnvelope/shipInfo");
	foreach(XmlNode shipInfo in shipList)
	{
	    invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true));
	}

And finally, the items from the FulfillmentEnvelope were placed back under an Items element under the main Invoice element:

	// Load the items.
	fulfillment.Load(@"..\..\FulfillmentEnvelope.xml");
	
	// Create an Items element in the Invoice to add these under
	XmlElement items = invoice.CreateElement("Items");

	// Add the items back in under Items.
	XmlNodeList itemList = fulfillment.SelectNodes("/FulfillmentEnvelope/item");
	foreach(XmlNode item in itemList)
	{
	    items.AppendChild(invoice.ImportNode(item,true));
	}

	// Add it in.
	invoice.DocumentElement.AppendChild(items.Clone( ));

One item to be aware of when dealing with multiple XmlDocuments is that when you take a node from one XmlDocument, you cannot just append it as a child to a node in a different XmlDocument because the node has the context of the original XmlDocument. If you try to do this, you will get the following exception message:

	The node to be inserted is from a different document context.

To fix this, use the XmlDocument.ImportNode method, which will make a copy (deep or shallow) of the node you are bringing over to the new XmlDocument. For instance, when you add the shipping information like so:

	invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true)); 

this line takes the shipInfo node, clones it deeply, then appends it to the main invoice node.

See Also

See the "XmlDocument Class," "XmlElement Class," and "XmlAttribute Class" topics in the MSDN documentation.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows