Search code examples
c#xmllinq

XML Find then return a value from previous element


I'm working with a bizarrely constructed XMl document, missing ID's and ambiguous names etc. Hopefully my example XML document paints a proper picture for you. The real document is huge and sometimes is nested 10 or more layers deep a real eye sore.

What I need to do is find a specific value in a node called Supplier/Name. But to find this I need to find a value in Var/Value first then look up. In my example I need to find Var/Value CLR-111.

I don't know if it's best to do this in LINQ or using XML docs?? The closet I found was XMLNode using previous node. But I'm not sure how to find the location first then jump up two.

The easy part is to locate the element but have no idea how to look up two elements . This is where I bomb.

What I need to return is ACME.

<?xml version="1.0" encoding="utf-8"?>
<Uni>
  <Job ID="Job1">
    <Manufacturing ID="MPG-1">
        <Factory>
            <SKUGroups ID="SKU-72">
                <Supplier>
                  <Name>ACME</Name>
                  <Details address="123 Bobs Road" Zip="90210" />
                </Supplier>
                <Type>Paint</Type>
                <Var>
                  <Name>ColorID</Name>
                  <Value>CLR-111</Value>
                </Var>
                <Supplier>
                  <Name>TomInc</Name>
                  <Details address="555 Jayne Lane" Zip="65986" />
                </Supplier>
                <Type>Tire</Type>
                <Var>
                  <Name>ColorID</Name>
                  <Value>CLR-2222</Value>
                </Var>
            </SKUGroups>
        </Factory>
    </Manufacturing>
  </Job>
</Uni>


XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(myPath);
XmlNode blaa = doc.SelectSingleNode("descendant::SKUGroups[Var/Value='CLR-111']/Var/Name");

Solution

  • I never go backwards. Always forward. I like collection all the data and then use a dictionary to look up values.

    Below is using Xml linq. I cast instead of using value so if item is null I do not get exceptions

    See code below

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XDocument doc = XDocument.Load(FILENAME);
                List<Supplier> suppliers = new List<Supplier>();
                foreach(XElement job in doc.Descendants("Job"))
                {
                    string jobId = (string)job.Attribute("ID");
                    XElement manufacturing = job.Element("Manufacturing");
                    string manufacturingId = manufacturing == null ? "" : (string)manufacturing.Attribute("ID");
                    foreach(XElement skuGroup in job.Descendants("SKUGroups"))
                    {
                        string skuGroupId = (string)skuGroup.Attribute("ID");
                        Supplier supplier = null;
                        foreach(XElement element in skuGroup.Elements())
                        {
                            switch(element.Name.LocalName)
                            {
                                case "Supplier":
                                    supplier = new Supplier();
                                    suppliers.Add(supplier);
                                    supplier.jobId = jobId;
                                    supplier.manufacturingId = manufacturingId;
                                    supplier.skuGroupId = skuGroupId;
                                    supplier.name = (string)element.Element("Name");
                                    XElement details = element.Element("Details");
                                    supplier.address = details == null ? "" : (string)details.Attribute("address");
                                    supplier.zip = details == null ? "" : (string)details.Attribute("Zip");
                                    break;
                                case "Type":
                                    supplier.type = (string)element;
                                    break;
                                case "Var":
                                    supplier.varName = (string)element.Element("Name");
                                    supplier.value = (string)element.Element("Value");
                                    break;
    
                            }
    
                        }
                    }
    
                }
                Dictionary<string, Supplier> dict = suppliers.GroupBy(x => x.value).ToDictionary(x => x.Key, y => y.FirstOrDefault());
                Supplier CLR_2222 = dict["CLR-2222"];
     
            }
     
        }
        public class Supplier
        {
            public string jobId { get; set; }
            public string manufacturingId { get; set; }
            public string skuGroupId { get; set; }
            public string name { get; set; }
            public string address { get; set; }
            public string zip { get; set; }
            public string type { get; set; }
            public string varName { get; set; }
            public string value { get; set; }
        }
    }