Avatar of teknovation
teknovation
 asked on

XML node Challenge 3

All,

If there are two similar parent node or tag - which in this case is Address, the only difference is the first Address tag is meant for SHIP to address which is showned in the <AddressTypeCode> with value of "ST" and BILL TO address shown with "BT" value. How can I extract the tags based on the <AddressTypeCode> value?

If <AddressTypeCode> child tag in the <Address> parent tag equals ST= then show like this:

The ship to address is: Pepsi Distribution Center, ABC 123, Safeway 7th Street, Suite 1, Detroit, MI, 12345, USA
If <AddressTypeCode> child tag in the <Address> parent tag equals BT= then show like this:
The bill to address is: Main Headquarters, Attn: John, 147 Dupont Ave, Dock 123, Orlando, FL, 12345, USA

XML Code:
<?xml version="1.0" encoding="UTF-8"?>
<Orders xmlns="www.test-inc.com">
	<Order>
		<Header>
			<Address>
				<AddressTypeCode>ST</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressName>Pepsi Distribution Center</AddressName>
				<AddressAlternateName>ABC 123</AddressAlternateName>
				<Address1>Safeway 7th Street</Address1>
				<Address2>Suite 1</Address2>
				<City>Detroit</City>
				<State>MI</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
			</Address>
			<Address>
				<AddressTypeCode>BT</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressLocationNumber>15513432</AddressLocationNumber>
				<AddressName>Main Headquarters</AddressName>
				<AddressAlternateName>Attn: John</AddressAlternateName>
				<Address1>147 Dupont Ave</Address1>
				<Address2>Dock 123</Address2>
				<City>Orlando</City>
				<State>FL</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
				<Contact>
					<ContactTypeCode>BD</ContactTypeCode>
					<ContactName>Jane Doe</ContactName>
					<PrimaryPhone>111-222-3333</PrimaryPhone>
					<PrimaryFax>111-222-3333</PrimaryFax>
					<PrimaryEmail>buyer@TEST.com</PrimaryEmail>
				</Contact>
			</Address>
		</Header>

Open in new window



C# code: My attempt to write it
 // Ship to Address  
                        var addresses = xdoc.XPathSelectElements("//testinc:Address", nsmgr);//Get all the address childs : 2 in this case 
                        List<string> listofAddresses = new List<string>();//List to store the addresses 
                        foreach (XElement address in addresses)//For each line item
                        { 
                             //something here?
                        }

Open in new window

XMLC#.NET Programming

Avatar of undefined
Last Comment
teknovation

8/22/2022 - Mon
Russ Suter

You have a good start. Just run a logic check inside the foreach statement. Check to see if an element <AddressTypeCode> exists as a child of the XElement "address" and if so, what its value is. You can then act accordingly.
Fernando Soto

Hi teknovation;

See if this will meet your needs.
// Load Document
XDocument xdoc = XDocument.Load("Path and file name of the XML doc");
// Get a ref to the XML Namespace
XNamespace ns = xdoc.Root.Name.Namespace;

// Get the Ship to Address  
var addresses = xdoc.Root.Descendants(ns + "Address").ToList();

// If you want the Ship To address it will be element 0 and the Bill To address is element 1
// XDocuments are process in document order 
int idx = 0;  // 0 = ST and 1 = BT
// Get the proper address
XElement addInfo = addresses[idx];
Console.WriteLine("{0} {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8} ",
    (idx == 0) ? "The ship to address is:" : " The bill to address is:",
    addInfo.Element(ns + "AddressName").Value, 
    addInfo.Element(ns + "AddressAlternateName").Value,
    addInfo.Element(ns + "Address1").Value,
    addInfo.Element(ns + "Address2").Value,
    addInfo.Element(ns + "City").Value,
    addInfo.Element(ns + "State").Value,
    addInfo.Element(ns + "PostalCode").Value,
    addInfo.Element(ns + "Country").Value
    );

Open in new window

Russ Suter

@Fernando - Your code assumes that the ship to address will come first. There is absolutely no guarantee of that in the XML. You will need to look at the actual element value to correctly determine if it's a bill to or ship to address.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Fernando Soto

@Russ - The question states, "the only difference is the first Address tag is meant for SHIP to address which is showned in the <AddressTypeCode> with value of "ST" and BILL TO address shown with "BT" value", I have taken that to mean that the first of the two nodes is Ship to.
Russ Suter

In this case yes but you can't guarantee the input. The safer approach would be to check the item value. The XML would still be 100% valid if the two address elements appeared in the reverse order but the output would be wrong. Coding to assumptions is never a good idea.
Karrtik Iyer

Something like below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Q28909699_TEKNOVATION_CleanCode
{
    class Program
    {
        static void Main(string[] args)
        {
            string filepath = @"E:\tsip\codeblocks_examples\expertsexchange\TotalEESolution\Q28909699_TEKNOVATION_CleanCode\TestAddress.xml";
            XDocument xdoc = XDocument.Load(filepath);
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
            XNamespace ns = "www.test-inc.com";            
            var stadd = (from saddress in xdoc.Descendants(ns + "Address")
                        where saddress.Element(ns + "AddressTypeCode").Value == "ST"
                        select new
                        {
                            Address = string.Join(",", saddress.Elements().Where(y => y.Name != ns + "AddressTypeCode" && y.Name != ns + "Contact").Select(z => z.Value).ToList())
                        });
            var btadd = (from saddress in xdoc.Descendants(ns + "Address")
                        where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                        select new
                        {
                            Address = string.Join(",", saddress.Elements().Where(y => y.Name != ns + "AddressTypeCode" && y.Name != ns + "Contact").Select(z => z.Value).ToList())
                        });
            //Assumption is that there shall always be one ship to address and bill to address at any case, else First() might fail.
            Console.WriteLine("The ship to address is: {0}", stadd.AsEnumerable().First().Address);
            Console.WriteLine("The bill to address is: {0}", btadd.AsEnumerable().First().Address);
            Console.ReadLine();           
        }
    }
}

Open in new window

Output:
1-6-2016-11-00-23-PM.png
XML Used:
<?xml version="1.0" encoding="UTF-8"?>
<Orders xmlns="www.test-inc.com">
	<Order>
		<Header>
			<Address>
				<AddressTypeCode>ST</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressName>Pepsi Distribution Center</AddressName>
				<AddressAlternateName>ABC 123</AddressAlternateName>
				<Address1>Safeway 7th Street</Address1>
				<Address2>Suite 1</Address2>
				<City>Detroit</City>
				<State>MI</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
			</Address>
			<Address>
				<AddressTypeCode>BT</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressLocationNumber>15513432</AddressLocationNumber>
				<AddressName>Main Headquarters</AddressName>
				<AddressAlternateName>Attn: John</AddressAlternateName>
				<Address1>147 Dupont Ave</Address1>
				<Address2>Dock 123</Address2>
				<City>Orlando</City>
				<State>FL</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
				<Contact>
					<ContactTypeCode>BD</ContactTypeCode>
					<ContactName>Jane Doe</ContactName>
					<PrimaryPhone>111-222-3333</PrimaryPhone>
					<PrimaryFax>111-222-3333</PrimaryFax>
					<PrimaryEmail>buyer@TEST.com</PrimaryEmail>
				</Contact>
			</Address>
		</Header>
    </Order>      
    </Orders>
                                

Open in new window

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
teknovation

ASKER
This is great Karrtik - but is there a way to just pull out 1 field at a time in this format?

I just quickly mock this up for the purpose of example, so the data isnt exactly matching the file output. But just to give you an idea on what I need.

The ship to address is: Main Headquarters
The Location Code Qualifier is: 51
The AddressAlternateName is: ABC 123  
The Address Name is Safeway 7th Street
Karrtik Iyer

What about contact details in Bill to address, do you want that to be also printed, and if it would help given an XML (since you said the first one in the question is just a mock up) what would be the expected output? In that contact details there is a contact code type as well as "BD". So before I suggest a modified version of my solution, wanted to what is  your exact output requirement given an XML?
<Contact>
					<ContactTypeCode>BD</ContactTypeCode>
					<ContactName>Jane Doe</ContactName>
					<PrimaryPhone>111-222-3333</PrimaryPhone>
					<PrimaryFax>111-222-3333</PrimaryFax>
					<PrimaryEmail>buyer@TEST.com</PrimaryEmail>
				</Contact>

Open in new window

teknovation

ASKER
Yes sorry both - i want to be able to access specific xml tags in both addresses
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
Karrtik Iyer

Something like below, which produces the below shown output, the previous code to join all the elements to one string is also present, so if you do not want it you can remove that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Q28909699_TEKNOVATION_CleanCode
{
    class Program
    {
        static void Main(string[] args)
        {
            string filepath = @"E:\tsip\codeblocks_examples\expertsexchange\TotalEESolution\Q28909699_TEKNOVATION_CleanCode\TestAddress.xml";
            XDocument xdoc = XDocument.Load(filepath);
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
            XNamespace ns = "www.test-inc.com";
            nsmgr.AddNamespace("testinc", "www.test-inc.com");
            var stadd = (from saddress in xdoc.Descendants(ns + "Address")
                        where saddress.Element(ns + "AddressTypeCode").Value == "ST"
                        select new
                        {
                            Address = string.Join(",", saddress.Elements().Where(y => y.Name != ns + "AddressTypeCode" && y.Name != ns + "Contact").Select(z => z.Value).ToList())
                        });
            var btadd = (from saddress in xdoc.Descendants(ns + "Address")
                        where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                        select new
                        {
                            Address = string.Join(",", saddress.Elements().Where(y => y.Name != ns + "AddressTypeCode" && y.Name != ns + "Contact").Select(z => z.Value).ToList())
                        });
            //Assumption is that there shall always be one ship to address and bill to address at any case, else First() might fail
            Console.WriteLine("The ship to address is: {0}", stadd.AsEnumerable().First().Address);
            Console.WriteLine("The bill to address is: {0}", btadd.AsEnumerable().First().Address);

            var staddelements = (from saddress in xdoc.Descendants(ns + "Address")
                        where saddress.Element(ns + "AddressTypeCode").Value == "ST"
                        select saddress.Descendants()).FirstOrDefault();
            Console.WriteLine();
            Console.WriteLine("*****************************Shipping Address Start**********************************");
            foreach (var line in staddelements){
                Console.WriteLine(line.Name.LocalName + " : " + line.Value);

            }
            Console.WriteLine("*****************************Shipping Address End**********************************");
            Console.WriteLine();
            Console.WriteLine("*****************************Billing Address Start**********************************");
            var btaddelements = (from saddress in xdoc.Descendants(ns + "Address")
                         where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                         select saddress.Descendants()).FirstOrDefault();

            foreach (var line in btaddelements)
            {
                Console.WriteLine(line.Name.LocalName + " : " + line.Value);
            }
            Console.WriteLine("*****************************Billing Address End**********************************");
            Console.ReadLine();           
        }
    }
}

Open in new window

XML File
<?xml version="1.0" encoding="UTF-8"?>
<Orders xmlns="www.test-inc.com">
	<Order>
		<Header>
			<Address>
				<AddressTypeCode>ST</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressName>Pepsi Distribution Center</AddressName>
				<AddressAlternateName>ABC 123</AddressAlternateName>
				<Address1>Safeway 7th Street</Address1>
				<Address2>Suite 1</Address2>
				<City>Detroit</City>
				<State>MI</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
			</Address>
			<Address>
				<AddressTypeCode>BT</AddressTypeCode>
				<LocationCodeQualifier>51</LocationCodeQualifier>
				<AddressLocationNumber>15513432</AddressLocationNumber>
				<AddressName>Main Headquarters</AddressName>
				<AddressAlternateName>Attn: John</AddressAlternateName>
				<Address1>147 Dupont Ave</Address1>
				<Address2>Dock 123</Address2>
				<City>Orlando</City>
				<State>FL</State>
				<PostalCode>12345</PostalCode>
				<Country>USA</Country>
				<Contact>
					<ContactTypeCode>BD</ContactTypeCode>
					<ContactName>Jane Doe</ContactName>
					<PrimaryPhone>111-222-3333</PrimaryPhone>
					<PrimaryFax>111-222-3333</PrimaryFax>
					<PrimaryEmail>buyer@TEST.com</PrimaryEmail>
				</Contact>
			</Address>
		</Header>
    </Order>      
    </Orders>
                                

Open in new window

Output: 1-8-2016-7-01-12-PM.png
teknovation

ASKER
wow this works - but far more advance that I wanted.

How can I just say pull one node from the Address child xml nodes?

Instead of doing a for each, I need to just grab say the "Contact Name" or "primary phone" value?
Karrtik Iyer

Something like below.
            var btaddreselements = (from saddress in xdoc.Descendants(ns + "Address")
                                 where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                                 select saddress).FirstOrDefault();
            //Address Name
            var addressname = btaddreselements.Descendants(ns + "AddressName").FirstOrDefault();
            Console.WriteLine(addressname.Name.LocalName + " : " + addressname.Value);
            //Contact Name
            var contactName = btaddreselements.Descendants(ns + "ContactName").FirstOrDefault();
            Console.WriteLine(contactName.Name.LocalName + " : " + contactName.Value);
            //PrimaryPhone
            var primaryphone = btaddreselements.Descendants(ns + "PrimaryPhone").FirstOrDefault();
            Console.WriteLine(primaryphone.Name.LocalName + " : " + primaryphone.Value);

Open in new window

⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
teknovation

ASKER
hmm the address name is null.    

   var staddelements = (from saddress in xdoc.Descendants(ns + "Address")
                                             where saddress.Element(ns + "AddressTypeCode").Value == "ST"
                                             select saddress.Descendants()).FirstOrDefault();  
                       
                       
                        var btaddelements = (from saddress in xdoc.Descendants(ns + "Address")
                                             where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                                             select saddress.Descendants()).FirstOrDefault();

                        var addressname = btaddelements.Descendants(ns + "AddressName").FirstOrDefault(); 
                        Console.WriteLine(addressname.Name.LocalName + " : " + addressname.Value);

Open in new window

Karrtik Iyer

Can you share your XML that you used, because the XML that you gave me had a node called AddressName?
teknovation

ASKER
its the same xml, there is an addressname
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
Karrtik Iyer

I have posted the XML that I used, and using that the above code, here is the output I get for AddressName. Please check your namespace ns and see what it is set to, based on my XML I have set it to "www.test-inc.com".
 1-8-2016-9-59-42-PM.png1-8-2016-10-00-11-PM.png
teknovation

ASKER
This strange:

         var staddelements = (from saddress in xdoc.Descendants(ns + "Address")
                                             where saddress.Element(ns + "AddressTypeCode").Value == "ST"
                                             select saddress.Descendants()).FirstOrDefault();  
                       
                       
                        var btaddelements = (from saddress in xdoc.Descendants(ns + "Address")
                                             where saddress.Element(ns + "AddressTypeCode").Value == "BT"
                                             select saddress.Descendants()).FirstOrDefault();
                           

                            foreach (var line in btaddelements)
                            {
                                Console.WriteLine(line.Name.LocalName + " : " + line.Value);
                            }
                             var addressname = btaddelements.Descendants(ns + "Address1").FirstOrDefault();
Console.WriteLine(addressname.Name.LocalName + " : " + addressname.Value); ;


This whole line of code produces what I want except the very last line where it's bolded the var addressname is NULL. It produces the line by line item as shown for the foreach statement.
ASKER CERTIFIED SOLUTION
Karrtik Iyer

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
teknovation

ASKER
thanks so much it worked!
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.