0

I've got a project where I'm currently implementing support for reading values from an XML file via an arbitrary/user-defined path within the document's keys.

For example, if the document looks like this:

<information>
    <machine>
        <foo></foo>
        <name>
            test machine
        </name>
        <bar>spam</bar>
    </machine>
</information>

then the user might want to retrieve the value from the name key in information/machine.

Is there a way using XDocument/XPath that I can look up the values the user wants without knowing/coding in the schema for the document?

My initial thought was working through the document with a form of recursive function utilizing XElement items, but I feel like there ought to be a simpler/cleaner solution that doesn't require me rolling my own lookup code.

I also tried something along these lines

var doc = XDocument.Load("C:\Path\to\XML\file.xml");

// Split the parent keys string
XElement elem = doc.Root.XPathSelectElement("path/to/key");
if (elem != null && elem.Attribute("wantedKeyName") != null)
    replace = elem.Attribute("wantedKeyName").Value;

but elem is always null. I'm assuming there's a problem with the way I'm defining my path or utilizing XPathSelectElement, but I haven't worked it out yet.

RPiAwesomeness
  • 5,009
  • 10
  • 34
  • 52
  • XDocument doc = XDocument.Load("YOURXML.xml"); var q = from elements in doc.Elements("information").Elements("machine") select elements; foreach (var item in q) { string str = item.Element("name").Value;} – er-sho Aug 29 '18 at 01:43
  • @ershoaib That would be what I would do if I knew the schema of the XML file beforehand, but the program has to be able to handle any XML file and any path within that, without knowing the contents. – RPiAwesomeness Aug 29 '18 at 01:46
  • if you dont know file's content , how to use XPathSelectElement("path/to/key") ? in this case you dont know the path – Z.R.T. Aug 29 '18 at 02:44
  • @RPiAwesomeness maybe your actual XML contains default namespace ('attribute' named `xmlns` like `xmlns="some-namespace-uri-here"`). If this is the case see: [Use XPath with XML namespace](https://stackoverflow.com/questions/25396140/use-xpath-with-xml-namespace/25396543#25396543) – har07 Aug 29 '18 at 04:35
  • Use descendants : List elements = doc.Descendants("name").ToList(); – jdweng Aug 29 '18 at 09:25

2 Answers2

0
static XmlNode SearchNode(XmlNodeList nodeList, string nodeName)
{
    for (int i = 0; i < nodeList.Count; i++)
    {
        if (nodeList[i].Name == nodeName)
        {
            return nodeList[i];
        }

        if (nodeList[i].HasChildNodes)
        {
            XmlNode node = SearchNode(nodeList[i].ChildNodes, nodeName);
            if (node != null)
            {
                return node;
            }
        }
    }

    return null;
}

static XmlNodeList SearchNodeByPath(XmlNodeList nodeList, string xPath)
{
    for (int i = 0; i < nodeList.Count; i++)
    {
        var nodes = nodeList[i].SelectNodes(xPath);
        if (nodes != null && nodes.Count > 0)
        {
            return nodes;
        }

        if (nodeList[i].HasChildNodes)
        {
            XmlNodeList innerNodes = SearchNodeByPath(nodeList[i].ChildNodes, xPath);
            if (innerNodes != null && innerNodes.Count > 0)
            {
                return innerNodes;
            }
        }
    }

    return null;
}

this is using of methods :

    var node = SearchNode(doc.ChildNodes, "compiler");
    var node1 = SearchNodeByPath(doc.ChildNodes, "compilers/compiler");
Z.R.T.
  • 1,543
  • 12
  • 15
0

I turns out my solution using XPathSelectElement was the correct approach, I just had to prepend the path/to/key string with //.

The following code I ended up using does that and strips off any whitespace around the outside of the value (in case the value is on a separate line than the opening tag.

// xml is a struct with the path to the parent node (path/to/key)
// and the key name to look up
// Split the parent keys string
XElement elem = doc.Root.XPathSelectElement("//" + xml.KeyPath);
if (elem != null && elem.Element(xml.Key) != null)
    replace = elem.Element(xml.Key).Value.Trim();
RPiAwesomeness
  • 5,009
  • 10
  • 34
  • 52