Home > Software engineering >  XML Find then return a value from previous element
XML Find then return a value from previous element

Time:12-02

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");

CodePudding user response:

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; }
    }
}

CodePudding user response:

You're going to want to search for the CLR-111 node (probably with recursion), and once you find it, you can climb the tree with node.ParentNode.ParentNode to get back up to the Var node.

Once at the Var node you can use node.PreviousSibling to walk backwards in the XmlDoc.

for example:

var clrXmlTextElement = ... // whatever the XmlText object element of the <Value>CLR-111</Value> node is when you find it.
var valueNode = clrNode.ParentNode; // <Value>CLR-111</Value>
var varNode = valueNode.ParentNode; // <Var>
var typeNode = varNode.PreviousSibling; // <Type>Paint</Type>
var supplierNode = typeNode.PreviousSibling; // <Supplier>
var supplierName = GetSupplierName(supplierNode); // ACME

string GetSupplierName(XmlNode supplierNode)
{
    foreach (XmlElement xmlElement in supplierNode.ChildNodes)
    {
        if (xmlElement.Name == "Name")
        {
            return xmlElement.InnerText;
        }
    }

    return "Error: no <Name> tag found in <Supplier> node.";
}
  • Related