You 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.
In 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 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 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 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: {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>
In 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.