I have the following code to find xml elements that have a particular attribute value:
IEnumerable<XElement> elems =
xmlDoc.Descendants("elemName")
.Where(x =>
x.Attribute("attrName").Value
.Equals(
filename,
StringComparison.OrdinalIgnoreCase
)
);
The problem is that if the element does not have that attribute, according to the docs, it will return null and we'll get a crash when we try to do null.Value
.
I've rewritten it like this:
IEnumerable<XElement> elems =
xmlDoc.Descendants("elemName")
.Where(x =>
((x.Attribute("attrName") == null)
? ""
: x.Attribute("attrName").Value).Equals(
filename,
StringComparison.OrdinalIgnoreCase
)
);
But I think it's a bit ugly. Is there an idiomatic way of doing this - I imagine this is a fairly common operation? I suppose I could just do a regular loop.
2 Answers 2
I think you could use the null-conditional operator for x.Attribute("attrName").Value
and swap arguments of String.Equals
to avoid the NullReferenceException
as follows:
IEnumerable<XElement> elems =
xmlDoc.Descendants("elemName")
.Where(x =>
filename.Equals(
x.Attribute("attrName")?.Value,
StringComparison.OrdinalIgnoreCase
)
);
There is a trick - you can cast XAttribute to some data types: string, int, long, Guid, DateTime, bool etc and their nullable brothers. And if type supports null
value there will be no NullReferenceException. You will simply get null
as result:
(string)x.Attribute("attrName")
Next: I suggest you to use more meaningful names for variables, even if it's one-letter variable. I.e. instead of x
for element you can use e
.
And last, you can write following extension method to nicely compare strings
public static bool EqualsIgnoreCase(this string s, string value)
{
return String.Equals(s, value, StringComparison.OrdinalIgnoreCase);
}
And finally your parsing query will look like
from e in xmlDoc.Descendants("elemName")
where filename.EqualsIgnoreCase((string)e.Attribute("attrName"))
select e;