Recipe 17.9 Tearing Apart an XML Document
Problem
You have an XML document that needs to
be broken apart into its constituent parts. Each part can then be
sent to a different destination (possibly a web service) to be
processed individually. This solution is useful when you have a large
document, such as an invoice, in XML form. For example, with an
invoice, you would want to tear off the billing information and send
this to accounting while sending the shipping information to
shipping, and then send the invoice items to fulfillment to be
processed.
Solution
In order to
separate the invoice items, we will load an
XmlDocument with the invoice XML from the
Invoice.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Invoice 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>
<billInfo>
<name>Beerly Standing</name>
<attn>Accounting</attn>
<street>98 North Street</street>
<city>Intox</city>
<state>NH</state>
</billInfo>
<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>
The code to tear this invoice apart, and send the various information
pieces to their respective departments, is shown
here:
public static void ProcessInvoice( )
{
XmlDocument xmlDoc = new XmlDocument( );
// pick up invoice from deposited directory
xmlDoc.Load(@"..\..\Invoice.xml");
// get the Invoice element node
XmlNode Invoice = xmlDoc.SelectSingleNode("/Invoice");
// get the invoice date attribute
XmlAttribute invDate =
(XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceDate");
// get the invoice number attribute
XmlAttribute invNum =
(XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceNumber");
// Process the billing information to Accounting
XmlElement billingEnvelope = xmlDoc.CreateElement("BillingEnvelope");
// correlate this information back to the original invoice number and date
billingEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
billingEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList billList = xmlDoc.SelectNodes("/Invoice/billInfo");
// add the billing information to the envelope
foreach(XmlNode billInfo in billList)
{
billingEnvelope.AppendChild(billInfo.Clone( ));
}
Console.WriteLine("BillingEnvelope:\r\n{0}",billingEnvelope.OuterXml);
// Save a copy of the envelope
FileStream fileStream = File.Create(@"..\..\BillingEnvelope.xml");
byte [] bytes = Encoding.ASCII.GetBytes(billingEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
// Process the shipping information to Shipping
XmlElement shippingEnvelope = xmlDoc.CreateElement("ShippingEnvelope");
// correlate this information back to the original invoice number and date
shippingEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
shippingEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList shipList = xmlDoc.SelectNodes("/Invoice/shipInfo");
// add the shipping information to the envelope
foreach(XmlNode shipInfo in shipList)
{
shippingEnvelope.AppendChild(shipInfo.Clone( ));
}
Console.WriteLine("ShippingEnvelope:\r\n{0}",shippingEnvelope.OuterXml);
// Save a copy of the envelope
fileStream = File.Create(@"..\..\ShippingEnvelope.xml");
bytes = Encoding.ASCII.GetBytes(shippingEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
// Process the item information to Fulfillment
XmlElement fulfillmentEnvelope = xmlDoc.CreateElement("FulfillmentEnvelope");
// correlate this information back to the original invoice number and date
fulfillmentEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
fulfillmentEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList itemList = xmlDoc.SelectNodes("/Invoice/Items/item");
// add the item information to the envelope
foreach(XmlNode item in itemList)
{
fulfillmentEnvelope.AppendChild(item.Clone( ));
}
Console.WriteLine("FulfillmentEnvelope:\r\n{0}",fulfillmentEnvelope.OuterXml);
// Save a copy of the envelope
fileStream = File.Create(@"..\..\FulfillmentEnvelope.xml");
bytes = Encoding.ASCII.GetBytes(fulfillmentEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
// Now send the data to the web services ...
}
The "envelopes" containing the
various pieces of XML data for the web services are 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>
Discussion
In order to tear apart the invoice, we needed to establish what
pieces would go to which departments. The breakdown of this is that
each of the envelopes would get the invoice date and invoice number
from the main invoice to give context to the information in the
envelope. The billInfo element and children would
go to the BillingEnvelope, the
shipInfo element and children would go to the
ShippingEnvelope, and the item
elements would go to the FulfillmentEnvelope. Once
these envelopes were constructed, they would be sent to the web
services for each department that accepts the data to perform their
function for this invoice.
In the example program from the solution, we first loaded the
Invoice.xml file and got the attributes we were
going to give to each of the envelopes:
XmlDocument xmlDoc = new XmlDocument( );
// pick up invoice from deposited directory
xmlDoc.Load(@"..\..\Invoice.xml");
// get the Invoice element node
XmlNode Invoice = xmlDoc.SelectSingleNode("/Invoice");
// get the invoice date attribute
XmlAttribute invDate =
(XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceDate");
// get the invoice number attribute
XmlAttribute invNum =
(XmlAttribute)Invoice.Attributes.GetNamedItem("invoiceNumber");
Then we established each envelope with the sections of the invoice
that matter to the respective functions (the
BillingEnvelope is handled by Accounting, the
ShippingEnveloper is handled by Shipping, and the
FulfillmentEnvelope is handled by
Fulfillment, starting with the
BillingEnvelope:
// Process the billing information to Accounting
XmlElement billingEnvelope = xmlDoc.CreateElement("BillingEnvelope");
// correlate this information back to the original invoice number and date
billingEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
billingEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList billList = xmlDoc.SelectNodes("/Invoice/billInfo");
// add the billing information to the envelope
foreach(XmlNode billInfo in billList)
{
billingEnvelope.AppendChild(billInfo.Clone( ));
}
Console.WriteLine("BillingEnvelope:\r\n{0}",billingEnvelope.OuterXml);
// Save a copy of the envelope
FileStream fileStream = File.Create(@"..\..\BillingEnvelope.xml");
byte [] bytes = Encoding.ASCII.GetBytes(billingEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
Then the ShippingEnvelope was created:
// Process the shipping information to Shipping
XmlElement shippingEnvelope = xmlDoc.CreateElement("ShippingEnvelope");
// correlate this information back to the original invoice number and date
shippingEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
shippingEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList shipList = xmlDoc.SelectNodes("/Invoice/shipInfo");
// add the shipping information to the envelope
foreach(XmlNode shipInfo in shipList)
{
shippingEnvelope.AppendChild(shipInfo.Clone( ));
}
Console.WriteLine("ShippingEnvelope:\r\n{0}",shippingEnvelope.OuterXml);
// Save a copy of the envelope
fileStream = File.Create(@"..\..\ShippingEnvelope.xml");
bytes = Encoding.ASCII.GetBytes(shippingEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
Finally, the FulfillmentEnvelope was created:
// Process the item information to Fulfillment
XmlElement fulfillmentEnvelope = xmlDoc.CreateElement("FulfillmentEnvelope");
// correlate this information back to the original invoice number and date
fulfillmentEnvelope.Attributes.Append((XmlAttribute)invDate.Clone( ));
fulfillmentEnvelope.Attributes.Append((XmlAttribute)invNum.Clone( ));
XmlNodeList itemList = xmlDoc.SelectNodes("/Invoice/Items/item");
// add the item information to the envelope
foreach(XmlNode item in itemList)
{
fulfillmentEnvelope.AppendChild(item.Clone( ));
}
Console.WriteLine("FulfillmentEnvelope:\r\n{0}",fulfillmentEnvelope.OuterXml);
// Save a copy of the envelope
fileStream = File.Create(@"..\..\FulfillmentEnvelope.xml");
bytes = Encoding.ASCII.GetBytes(fulfillmentEnvelope.OuterXml);
fileStream.Write(bytes,0,bytes.Length);
fileStream.Close( );
At this point, each of the envelopes could be posted to the
respective web service interfaces.
Note that when we appended the attributes from the
Invoice to the envelopes, we called the
XmlNode.Clone
method on the XmlAttributes. This is done so that
each of the elements had their own separate copy. If you do not do
this, then the attribute will appear only on the last element it was
assigned to.
See Also
See the "XmlDocument Class,"
"XmlElement Class," and
"XmlAttribute Class" topics in the
MSDN documentation.
|