Well, using .NET 3.5 and XDocument I am trying to find <table class='imgcr'> element. I created the code below but it crashes, of course, because e.Attribute("class") may be null. So... I have to put null check everywhere? This will double e.Attribute("class"). Not laconic solution at all.
XElement table =
d.Descendants("table").
SingleOrDefault(e => e.Attribute("class").Value == "imgcr");
If you are sure you exception is thrown because you table element may come without class attribute, then you could do this instead:
XElement table =
d.Descendants("table").
SingleOrDefault(e => ((string)e.Attribute("class")) == "imgcr");
In that case you are casting a null value to string, which is null at the end, so you are comparing null == "imgcr", what is false.
You can check this msdn page if you need more info about how to retrieve the value of an attribute. There you will find this affirmation:
You can cast an XAttribute to the desired type; the explicit
conversion operator then converts the contents of the element or
attribute to the specified type.
I guess this is quite short
XElement table =
d.Descendants("table").
SingleOrDefault(e => { var x = e.Attribute("class"); return x==null ? false: x.Value == "imgcr";});
this is shorter (but not much -- unless you can re-use t variable.)
XAttribute t = new XAttribute("class","");
XElement table =
d.Descendants("table").
SingleOrDefault(e => (e.Attribute("class") ?? t).Value == "imgcr");
Related
So I've got a collection of structs (it's actually a WCF datacontract but I'm presuming this has no bearing here).
List<OptionalExtra> OptionalExtras;
OptionalExtra is a struct.
public partial struct OptionalExtra
Now I'm running the below statement:
OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra != null)
{
}
Now this won't compile:
the operator != cannot be applied to opperands of type OptionalExtra
and '<null>'
After a little googling I realised it's because OptionalExtra is a struct. Which I believe is not nullable unless defined as a nullable type?
So my question is, if my where statement returns no results what will be the outcome of the FirstOrDefault call? Will it thrown an exception?
Incidently this should never happen but better safe than sorry.
If your collection is empty, FirstOrDefault will return default(OptionalExtras). The default value of a struct is the struct with all its values in turn default initialized (i.e. zero, null, etc.).
If you assume that there will be an element and your code doesn't work with an empty collection, Use First() instead, since that will throw an exception when your collection is empty. It's generally better to fail fast than to return wrong data.
If you cannot assume that there will be an element, but also cannot deal with struct default initialization, you might make the structs in the collection a nullable value type, for example as follows:
OptionalExtras
.Where(w => w.Code == optExtra.Code)
.Cast<OptionalExtra?>()
.FirstOrDefault();
This way you can get a null return even for a struct. The key idea here is to extend the set of possible values to include something other than an OptionalExtra to allow detection of an empty list. If you don't like nullables, you could instead use a Maybe<> implementation (not a .NET builtin), or use an empty-or-singleton list (e.g. .Take(1).ToArray(). However, a nullable struct is likely your best bet.
TL;DR;
.FirstOrDefault<T>() returns default(T) if the sequence is empty
Use .First() instead if you assume the list is non-empty.
Cast to nullable and then use .FirstOrDefault<T>() when you cannot assume the list is non-empty.
As others have said, the result of your code when no elements match will be:
default( OptionalExtra )
If you want a null returned, you can cast your list to OptionalExtra?
OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...
You can then test for null
If default(OptionExtra) is still a valid value, it's better to change your code to this
var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
if (results.Any()) {
multiOptExtra = results[0]
}
The result will be the default value of your struct, e.g. default(OptionalExtras).
Whereas for a reference type the default value is null.
its provide you defualt value for your structure like as below
int[] numbers = { };
int first = numbers.FirstOrDefault();
Console.WriteLine(first);//this print 0 as output
other option to handle is make use of default value like as below
List<int> months = new List<int> { };
// Setting the default value to 1 by using DefaultIfEmpty() in the query.
int firstMonth2 = months.DefaultIfEmpty(1).First();
Console.WriteLine("The value of the firstMonth2 variable is {0}", firstMonth2);
If you want to check for null, use System.Nullable collection:
var OptionalExtras = new List<OptionalExtra?>();
/* Add some values */
var extras = OptionalExtras.FirstOrDefault(oe => oe.Value.Code == "code");
if (extras != null)
{
Console.WriteLine(extras.Value.Code);
}
Note that you have to use Value to access the element.
Assuming Code is a string for the purposes of my answer, you should be able just to test that value for its default.
OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra.Code != null)
{
}
I have around a dozen solutions to this, but none seem to fit what I am trying to do. The XML file has elements that may not be in the file each time it is posted.
The trick is, the query is dependent upon a question value to get the answer value. Here is the code:
string otherphone = (
from e in contact.Descendants("DataElement")
where e.Element("QuestionName").Value == "other_phone"
select (string)e.Element("Answer").Value
).FirstOrDefault();
otherphone = (!String.IsNullOrEmpty(otherphone)) ? otherphone.Replace("'", "''") : null;
Under the "contact" collection, here are many elements named "DataElement", each with its own "QuestionName" and "Answer" elements, so I query to find the one where the element's QuestionName value is "other_phone", then I get the Answer value. Of course I will need to do this for each value I am seeking.
How can I code this to ignore the DataElement containing QuestionName with value of "other_phone" if it doesn't exist?
You can use Any method to check whether or not the elements exists :
if(contact.Descendants("DataElement")
.Any(e => (string)e.Element("QuestionName") == "other_phone"))
{
var otherPhone = (string)contact
.Descendants("DataElement")
.First(e => (string)e.Element("QuestionName") == "other_phone")
.Element("Answer");
}
Also, don't use Value property if you are using explicit cast.The point of explicit cast is avoid the possible exception if the element wasn't found.If you use both then before the cast, accessing the Value property will throw the exception.
Alternatively, you can also just use the FirstOrDefault method without Any, and perform a null-check:
var element = contact
.Descendants("DataElement")
.FirstOrDefault(e => (string)e.Element("QuestionName") == "other_phone");
if(element != null)
{
var otherPhone = (string)element.Element("Answer");
}
So you want to know if other_phone exists or not?
XElement otherPhone = contact.Descendants("QuestionName")
.FirstOrDefault(qn => ((string)qn) == "other_phone");
if (otherPhone == null)
{
// No question with "other_phone"
}
else
{
string answer = (string)otherPhone.Parent.Element("Answer");
}
This question already has answers here:
How can i check if this XElement is not null before adding it to an anonymous object?
(4 answers)
Closed 9 years ago.
I have a simple XML document that looks like this:
<Person>
<LastName>LastName1</LastName>
<FirstName>FirstName1</FirstName>
<MiddleName>MiddleName1</MiddleName>
<Suffix>Suffix1</Suffix>
</Person>
However I have a constraint where I am not allowed to add empty tags. Therefore if the Suffix value does not exist, I cannot use <Suffix /> or validation will fail.
I'm composing the XML structure using XElement objects from different classes that return their respective XML via a returned XElement object from a .ToXML() method. I need to check per element to see if the XElement being returned is null. If that's the case it has to be like that line never existed. I'm trying to use the ?? operator but I'm getting the error that ?? left operand is never null. I had the code as follows:
public XElement ToXML()
{
return new XElement("Employee",
new XElement(this.LastName.ToXML()) ?? null,
new XElement(this.FirstName.ToXML()) ?? null,
new XElement(this.MiddleName.ToXML()) ?? null,
new XElement(this.Suffix.ToXML()) ?? null);
}
How can I check per XML node to see if the XElement object being returned is null and if so ignore adding/composing that node all together? Any help is appreciated, thanks!
You should be using this code instead:
public XElement ToXML()
{
var children = new[]
{
this.LastName.ToXML(),
this.FirstName.ToXML(),
this.MiddleName.ToXML(),
this.Suffix.ToXML()
};
return new XElement("Employee", children.Where(x => x != null));
}
Please note that your code has several issues:
The null-coalescing operator (??) is an operator that returns the right hand value when the left hand value is null. Making the right hand value null is completely useless.
Mixing a new statement with the ?? operator is completely useless, too, because the result of new will never be null.
The new XElement part also seems pretty useless. I assume that ToXML already returns an XElement, so why create a new instance?
A constructor in C# will either return a non-null reference to the object or will throw an exception. It will not return null*.
As for your problem, why not:
return new XElement("Employee",
this.LastName.ToXML(),
this.FirstName.ToXML(),
this.MiddleName.ToXML(),
this.Suffix.ToXML());
And just have each of those ToXML methods return null if none exist?
Or if your case is the properties themselves are null:
return new XElement("Employee",
this.LastName != null ? this.LastName.ToXML() : null, /* null is ignored */
this.FirstName != null ? this.FirstName.ToXML() : null,
this.MiddleName != null ? this.MiddleName.ToXML() : null,
this.Suffix != null ? this.Suffix.ToXML() : null);
I also just realized perhaps you always get back an XElement, but it may be empty, in that case:
var elements = new[] { this.LastName.ToXML(), this.FirstName.ToXML(), ...
// use IsEmpty to filter out those that are <Element/>
return new XElement("Employee",
elements.Where(ee => ee != null && !ee.IsEmpty));
*I believe there is an interesting edge case where you could get this from a COM interface instantiation, but we'll ignore all "strange" coding.
I find it puzzling to determine the best way to parse some XML. It seems they are so many possible ways and none have really clicked with me.
My current attempt looks something like this:
XElement xelement = XElement.Parse(xmlText);
var name = xelement.Element("Employee").Attribute("name").Value;
So, this works. But it throws an exception if either the "Employee" element or the "name" attribute is missing. I don't want to throw an exception.
Exploring some examples available online, I see code like this:
XElement xelement = XElement.Load("..\\..\\Employees.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.Element("Name").Value);
}
This would seem to suffer from the exact same issue. If the "Name" element does not exist, Element() returns null and there is an error calling the Value property.
I need a number of blocks like the first code snippet above. Is there a simple way to have it work and not throw an exception if some data is missing?
You can use the combination of the explicit string conversion from XAttribute to string (which will return null if the operand is null) and the FirstOrDefault method:
var name = xelement.Elements("Employee")
.Select(x => (string) x.Attribute("name"))
.FirstOrDefault();
That will be null if either there's no such element (because the sequence will be empty, and FirstOrDefault() will return null) or there's an element without the attribute (in which case you'll get a sequence with a null element, which FirstOrDefault will return).
I often use extension methods in cases like this as they work even if the reference is null. I use a slightly modified version of the extension method's from Anders Abel's very good blog posting from early 2012 'Null Handling with Extension Methods':
public static class XElementExtension
{
public static string GetValueOrDefault(this XAttribute attribute,
string defaultValue = null)
{
return attribute == null ? defaultValue : attribute.Value;
}
public static string GetAttributeValueOrDefault(this XElement element,
string attributeName,
string defaultValue = null)
{
return element == null ? defaultValue : element.Attribut(attributeName)
.GetValueOrDefault(defaultValue);
}
}
If you want to return 'null' if the element or attribute doesn't exist:
var name = xelement.Element("Employee")
.GetAttributeValueOrDefault("name" );
If you want to return a default value if the element or attribute doesn't exist:
var name = xelement.Element("Employee")
.GetAttributeValueOrDefault("name","this is the default value");
To use in your for loop:
XElement xelement = XElement.Load("..\\..\\Employees.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.GetAttributeValueOrDefault("Name"));
}
You could always use XPath:
string name = xelement.XPathEvaluate("string(Employee/#name)") as string;
This will be either the value of the attribute, or null if either Employee or #name do not exist.
And for the iterative example:
foreach (XNode item in (IEnumerable)xelement.XPathEvaluate("Employee/Name"))
{
Console.WriteLine(item.Value);
}
XPathEvaluate() will only select valid nodes here, so you can be assured that item will always be non-null.
It all depends on what you want to do with the data once you've extracted it from the XML.
You would do well to look at languages that are designed for XML processing, such as XSLT and XQuery, rather than using languages like C#, which aren't (though Linq gives you something of a hybrid). Using C# or Java you're always going to have to do a lot of work to cope with the fact that XML is so flexible.
Use the native XmlReader. If your problem is reading large XML files instead of allowing the XElement to build an object representation, you can build something like Java SAX parser that only stream the XML.
Ex:
http://www.codeguru.com/csharp/csharp/cs_data/xml/article.php/c4221/Writing-XML-SAX-Parsers-in-C.htm
I am trying to figure out how to handle nodes that do not exist for all of my "card" elements. I have the following linq query:
FinalDeck = (from deck in xmlDoc.Root.Element("Cards")
.Elements("Card")
select new CardDeck
{
Name = deck.Attribute("name").Value,
Image = deck.Element("Image").Attribute("path").Value,
Usage = (int)deck.Element("Usage"),
Type = deck.Element("Type").Value,
Strength = (int)deck.Element("Ability") ?? 0
}).ToList();
with the Strength item, I had read another posting that the ?? handles the null. I am getting the following error though:
Operator '??' cannot be applied to operands of type 'int' and 'int'
How do I handle this issue?
Thanks!
Rather than use the Value property, cast to string... and for the int, cast to int? instead. The user defined conversions to nullable types will return null if the source XAttribute/XElement is null:
FinalDeck = (from deck in xmlDoc.Root.Element("Cards")
.Elements("Card")
select new CardDeck
{
Name = (string) deck.Attribute("name"),
Image = (string) deck.Element("Image").Attribute("path"),
Usage = (int?) deck.Element("Usage"),
Type = (string) deck.Element("Type"),
Strength = (int?) deck.Element("Ability") ?? 0
}).ToList();
Note that this won't help for the case where the Image element is missing, as then it'll try to dereference a null element to find the path attribute. Let me know if you want a workaround for that, but it'll be a bit of a pain, relatively speaking.
EDIT: You can always create an extension method for this yourself:
public static XAttribute NullSafeAttribute(this XElement element, XName name)
{
return element == null ? null : element.Attribute(name);
}
Then call it like this:
Image = (string) deck.Element("Image").NullSafeAttribute("path"),