LinQ to xml, specifying a route that can be null at any point

青春壹個敷衍的年華 提交于 2021-01-04 04:14:44

问题


<b>
  <c>
     <z> Wally  </z>
     <w> whatever </w>  
  </c>
  <d>
     <z> anotherValue </z>
  </d>
  <e> idValue </e>
</b>


class Example {
  public string Id {get; set;}
  public string name {get; set;}
}

Suppose we have the above xml and class. Im trying to create an object from the xml using LinQ to Xml.

I could achieve this with something like this:

var doc = XDocument.Load(xmlPath);
var root = doc.Root;

var anExample = new Example()
{
   Id = root.Element("e").Value,
   name = root.Element("c").Element("z").Value
};

So far so good, the problem is that 'c' and 'z' tags are optional. You could have no 'c' at all or 'c' without 'z'. Then I would find myself checking whether or not each of this tags are null in order to avoid calling the Element method and Value property on a null object. I mean:

var doc = XDocument.Load(xmlPath);
var root = doc.Root;

var anExample = new Example(){ Id = root.Element("e").Value };

var c = root.Element("c")

if(c != null) {
   var z = c.Element("z");
   if( z != null)
    anExample.name = z.Value;
}

And as the nested levels increase things get worst. I suppose there most be a better way to do what I want.


回答1:


You can use C# 6.0 null-conditional operator to ensure that all items in call chain are not null:

// C# 6 or higher:
string value = doc.Root?.Element("c")?.Element("z")?.Value;

The result of this expression will have a value if only all given elements exist, and will be null otherwise.

Another way is to use XPath extension methods from System.Xml.XPath namespace to find an element. It is more suitable if you use C# 6 or even older version since it looks nice without null-conditional operator:

// C# 6 or higher:
string value = doc.Root.XPathSelectElement("./c/z")?.Value;

// C# 5 or lower:
var element = doc.Root.XPathSelectElement("./c/z");
if (element != null)
{
    string value = element.Value;
}

The result will be the same since XPathSelectElement will return null if element does not exist.




回答2:


You should try to stay in the LINQ world of IEnumerable until the last possible moment.

This means using Elements instead of Element - this will return an empty sequence in the case the element is missing.

At the end, you can call SingleOrDefault to get the resulting element or null in the case one of the elements along the way was missing.

Note that there are explicit conversion operators that can convert an element to various types - this can be useful when you need conversion to something other than string and can also handle the case where the element is null.

var name = (string) root.Elements("c").Elements("z").SingleOrDefault()


来源:https://stackoverflow.com/questions/44801379/linq-to-xml-specifying-a-route-that-can-be-null-at-any-point

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!