问题
<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