[ Team LiB ] |
Recipe 17.10 Putting Together an XML DocumentProblemYou have various pieces of a document in XML form that need to be put together to form a single XML document—this is the opposite of what was done in Recipe 17.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. SolutionIn order to reconstitute the original invoice, we need to reverse the process used to create the pieces of the invoice using multiple XmlDocuments. There are three parts being sent back to us to help in reforming the original invoice XML: BillingEnvelope.xml, ShippingEnvelope.xml, and Fulfillment.xml. These are shown listed in the following sections: 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, we reverse the process we went through to break it apart, while inferring the invoice date and invoice number from the BillingEnvelope to help reestablish the invoice: public static void ReceiveInvoice( ) { 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); // 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 FileStream fileStream = File.Create(@"..\..\ReceivedInvoice.xml"); byte [] bytes = Encoding.ASCII.GetBytes(invoice.OuterXml); fileStream.Write(bytes,0,bytes.Length); fileStream.Close( ); } 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> DiscussionIn the Solution code, the first thing we did was to create a set of XmlDocuments for the Invoice, BillingEnvelope, ShippingEnvelope, and FulfillmentEnvelope. Then we created 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, we processed the BillingEnvelope first, taking the invoice date and number from it and adding it to the Invoice. Then we added 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, as shown, when we add the shipping information like so: invoice.DocumentElement.AppendChild(invoice.ImportNode(shipInfo,true)); This line takes the shipInfo node, clones it deeply, then it appends it to the main invoice node. See AlsoSee the "XmlDocument Class," "XmlElement Class," and "XmlAttribute Class" topics in the MSDN documentation. |
[ Team LiB ] |