DekGenius.com
[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section