Find or create an element by xpath using LINQ-to-XML
Asked Answered
C

2

7

Does anyone have a neat way of finding or creating an XObject using an xpath expression.

The problem I am having is that I need to set a value on an element (which I have the xpath for), which may or not be in existence. If it's not in existence I would like it to be created.

Any hints or links would be very much appreciated.

Thanks all.

Chacon answered 4/1, 2011 at 14:4 Comment(2)
XPath selects nodes, it doesn't serialize new documents nor update value of nodes. Besides that, this questions is bad defined: as @C. Lawrence Wenham has pointed out when //element is evaluated to an empty node set, where do you think the element should be created?Tenderfoot
@Alejandro. The question could be better phrased. Agreed. I am looking for functionality akin to that provided by JXPath in the Java world. See commons.apache.org/jxpath/users-guide.html#Creating_Objects. Using this API it is very much possible to define the node to be updated or created via xpath. In this API it is common to "combine creating a path with setting the value of the leaf: the createPathAndSetValue(path, value) method is used for that."Chacon
S
1

You can use the System.Xml.XPath.Extensions class to evaluate XPath expressions on an XDocument.

http://msdn.microsoft.com/en-us/library/system.xml.xpath.extensions.aspx

For example:

using System.Xml.XPath;
...
XDocument doc = XDocument.Load("sample.xml");
var matching = doc.XPathEvaluate("//Book[@Title='Great Expectations']");  
// 'matching' could be an IEnumerable of XElements, depending on the query
Sulfapyridine answered 4/1, 2011 at 14:44 Comment(3)
Thanks for the prompt response guys, but what I am looking for is the ability to create a node based on xpath where it doesn't. So for example I could do something like: var matching = doc.XPathSelectElement("//Book[@Title='Great Expectations']"); and in the event the element doesn't exist the thing is created for me.Chacon
I don't think that can be done, in .Net or in general, since XPath is a query language you can't deduce what single chain of nodes must be made to satisfy its condition, because there could be infinitely many valid hierarchies. In the example, "//Book" means "a Book element anywhere in the document", so if you were to create the node then where would you put it?Sulfapyridine
Point taken @C. Lawrence Wenham. I was being a bit lazy there, just using your example for reference. Where a single chain of nodes can be deduced (see my example in my response to @Alejandro), in this case, in the Java world I can use commons JXPath API to do this. That's what I am seeking to replicate with .NET.Chacon
J
1

Assuming a simple path, and you just want to add some data at the end of it.

Starting with some example data:

var xml = XDocument.Parse(@"<?xml version=""1.0""?>
<messages>
  <request type=""MSG"">
    <header>
      <datestamp>2019-02-26T14:49:41+00:00</datestamp>
      <source>1</source>
    </header>
    <body>
      <title>Hi there</title>
    </body>
  </request>
</messages>
");

This won't work because the product node doesn't exist:

xml.XPathSelectElement("/messages/request/body/product")
    ?.Add(new XElement("description", "A new product"));

To do this you could define your own extension method:

public static class extensionMethods
{
    public static XElement FindOrAddElement(this XContainer xml, string nodeName)
    {
        var node = xml.Descendants().FirstOrDefault(x => x.Name == nodeName);
        if (node == null)
            xml.Add(new XElement(nodeName));
        return xml.Descendants().FirstOrDefault(x => x.Name == nodeName);
    }
}

And chain these together to create your new path.

xml.FindOrAddElement("messages")
   .FindOrAddElement("request")
   .FindOrAddElement("body")
   .FindOrAddElement("product")
   ?.Add(new XElement("description", "A new product"));
Jurisprudent answered 1/3, 2019 at 16:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.