Why is this program not accessing child nodes?

295 views Asked by At

Here it gets the XML document and individual nodes, and inserts the nodes into a dictionary.

//create the xml document obj
XmlDocument inputXMLDoc = new XmlDocument();
fileref.isValid = false;
//load the xml document
#region
try
{
    inputXMLDoc.XmlResolver = null;
    inputXMLDoc.Load( strfile );//load the xml file
    string input = inputXMLDoc.OuterXml;//get the string

    Console.WriteLine( "success,loaded XML" );
    logger.Log( "loaded xml:" + strfile );


    fileref.importList = new Dictionary<string, XmlNode>();

    nodeNames = new List<string> { "OrderId", "CustomerId", "CustomerName", "Addresses", "OrderStatus", "DateOrdered", "PaymentTime", "IncludeVAT", "OrderTotalIncVat", "OrderTotalVat", "Currency", "TypeOfSaleId" };

    try
    {
        int i = 0;
        foreach( string name in nodeNames )
        {
            Console.WriteLine( "Adding xml node " + name );

            if( inputXMLDoc.GetElementsByTagName( name ) != null )
            {
                XmlNodeList xlist = inputXMLDoc.GetElementsByTagName( name );

                foreach( XmlNode node in xlist )
                {
                    fileref.importList.Add( name, node );
                    //add individual node within nodelist
                    Console.WriteLine( name );

                }
            } //add specified node from XML doc
            else
            {
                nodeNames.RemoveAt( i );
            }
            i++;
        }
    }
}

Later, the nodes are accessed to save the information to a web service. However, nodes with child nodes within are not showing up this way.

 Invoices.Address address = new Invoices.Address();

 XmlNodeList oNodeList = fileref.importList["Addresses"].SelectNodes("/Delivery/Street");
 foreach (XmlNode xn in oNodeList)
 {
     address.Street = xn.InnerText;
 }

Sample XML document

<?xml version="1.0" encoding="utf-8"?>
<InvoiceOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <OrderId xmlns="http://24sevenOffice.com/webservices">35</OrderId>
  <CustomerId xmlns="http://24sevenOffice.com/webservices">21</CustomerId>
  <CustomerName xmlns="http://24sevenOffice.com/webservices">James Bond</CustomerName>
  <Addresses xmlns="http://24sevenOffice.com/webservices">
    <Delivery>
      <Street>11 Shewell Walk</Street>
      <State />
      <PostalCode>CO1 1WG</PostalCode>
      <PostalArea>Essex</PostalArea>
      <Name />
      <City>Colchester</City>
      <Country>UK</Country>
    </Delivery>
    <Invoice>
      <Street>10 Shewell Walk</Street>
      <State />
      <PostalCode>CO1 1WG</PostalCode>
      <PostalArea>Essex</PostalArea>
      <Name />
      <City>Colchester</City>
      <Country>UK</Country>
    </Invoice>
  </Addresses>
  <OrderStatus xmlns="http://24sevenOffice.com/webservices">Offer</OrderStatus>
  <DateOrdered xmlns="http://24sevenOffice.com/webservices">2015-06-15T14:00:00Z</DateOrdered>
  <PaymentTime xmlns="http://24sevenOffice.com/webservices">14</PaymentTime>
  <IncludeVAT xsi:nil="true" xmlns="http://24sevenOffice.com/webservices" />
  <OrderTotalIncVat xmlns="http://24sevenOffice.com/webservices">480.0000</OrderTotalIncVat>
  <OrderTotalVat xmlns="http://24sevenOffice.com/webservices">80.0000</OrderTotalVat>
  <Currency xmlns="http://24sevenOffice.com/webservices">
    <Symbol>LOCAL</Symbol>
  </Currency>
  <TypeOfSaleId xmlns="http://24sevenOffice.com/webservices">-100</TypeOfSaleId>
  <InvoiceRows xmlns="http://24sevenOffice.com/webservices">
    <InvoiceRow>
      <ProductId>18</ProductId>
      <RowId>4665754</RowId>
      <Price>400.0000</Price>
      <Name>17" Laptop Screen</Name>
      <DiscountRate>0.0000</DiscountRate>
      <Quantity>7.0000</Quantity>
      <Cost>0.0000</Cost>
      <InPrice>0.0000</InPrice>
    </InvoiceRow>
  </InvoiceRows>
</InvoiceOrder>
2

There are 2 answers

0
Charles Mager On BEST ANSWER

The reason your code doesn't work is likely that you're ignoring the namespace of the elements you're looking for. There are many questions covering how to do that, such as this one.

That said, XmlDocument is a creaky old API and the newer LINQ to XML is a huge improvement - I'd suggest you look into that.

I'm also not sure the dictionary is pulling its weight for such a small number of elements. You can simply query what you need straight from the XML. For example, to get all your fields as typed values:

var doc = XDocument.Parse(strfile);      
var order = doc.Elements("InvoiceOrder").Single();

XNamespace ns = "http://24sevenOffice.com/webservices";

var orderId = (int)order.Element(ns + "OrderId");
var customerId = (int)order.Element(ns + "CustomerId");
var customerName = (string)order.Element(ns + "CustomerName");
var orderStatus = (string)order.Element(ns + "OrderStatus");
var dateOrdered = (DateTime)order.Element(ns + "DateOrdered");
var paymentTime = (int)order.Element(ns + "PaymentTime");
var totalIncVat = (decimal)order.Element(ns + "OrderTotalIncVat");
var totalVat = (decimal)order.Element(ns + "OrderTotalVat");
var currency = (string)order.Elements(ns + "Currency").Elements(ns + "Symbol").SingleOrDefault();
var typeOfSaleId = (int)order.Element(ns + "TypeOfSaleId");

You can use a similar technique to get map your addresses to your strongly typed Address class:

var deliveryAddress = order.Elements(ns + "Addresses")
    .Elements(ns + "Delivery")
    .Select(e => new Invoice.Address
    {
        Street = (string)e.Element(ns + "Street"),
        // ....
    })
    .Single();
0
Chris On

The problem you have is with namespaces. If you specify the namespace for each of those elements then it seems to work. I came to this conclusion with a bit of googling and some experimentation so my explanation might not be spot on so I advise researching the issue further yourself to understand it correctly.

This code will work:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(inputXMLDoc.NameTable);
nsmgr.AddNamespace("ns", "http://24sevenOffice.com/webservices");
var oNodeList = importList["Addresses"].SelectNodes("//ns:Delivery/ns:Street",nsmgr);

The reason is (I think) that in your XML document you are specifying a default namespace for your elements (xmlns="http://24sevenOffice.com/webservices") and in your xpath you are not specifying that same namespace. In my code I create a namespace manager with that namespace in and prefix it to the two elements which it now considers to match the ones in your document that have these namespaces.