FROM#?? zero coalescing LINQ operator

I am trying to prevent NULL values ​​when parsing an XML file into a user object using LINQ.

I found a great solution for this on Scott Goog's blog , but for some reason it doesn't work for integers with me. I think I used the same syntax, but it seems like I'm missing something. Oh, and for some reason, it works when node is not empty.

Below is a snippet of my code.

List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname") select new GrantAgresso() { Year = (int?)g.Element("yearnode") ?? 0, Subdomain = (string)g.Element("domainnode") ?? "" }).ToList(); 

Mistake:

The input string was not in the correct format.

If someone knows what I'm doing wrong, help :)

Edit: XML fragment (weird names, but this is not optional)

 <Agresso> <AgressoQE> <r3dim_value>2012</r3dim_value> <r0r0r0dim_value>L5</r0r0r0dim_value> <r7_x0023_province_x0023_69_x0023_V005>0</r7_x0023_province_x0023_69_x0023_V005> <r7_x0023_postal_code_x0023_68_x0023_V004 /> <r7_x0023_country_x0023_67_x0023_V003>1004</r7_x0023_country_x0023_67_x0023_V003> <r7_x0023_communitydistrict_x0023_70_x0023_V006>0</r7_x0023_communitydistrict_x0023_70_x0023_V006> </AgressoQE> </Agresso> 
+7
source share
6 answers

The following extension method returns 0 if the element is missing, the element is empty or contains a string that cannot be parsed for an integer:

  public static int ToInt(this XElement x, string name) { int value; XElement e = x.Element(name); if (e == null) return 0; else if (int.TryParse(e.Value, out value)) return value; else return 0; } 

You can use it as follows:

 ... Year = g.ToInt("r3dim_value"), ... 

Or, if you are willing to consider the cost of reflection and accept the default value of any type of value, you can use this extension method:

 public static T Cast<T>(this XElement x, string name) where T : struct { XElement e = x.Element(name); if (e == null) return default(T); else { Type t = typeof(T); MethodInfo mi = t.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, Type.DefaultBinder, new Type[] { typeof(string), t.MakeByRefType() }, null); var paramList = new object[] { e.Value, null }; mi.Invoke(null, paramList); return (T)paramList[1]; //returns default(T), if couldn't parse } } 

and use it:

 ... Year = g.Cast<int>("r3dim_value"), ... 
+1
source

This is an expression that throws:

 (int?)g.Element("yearnode") 

This is because if the actual text value of the node element is String.Empty and not null , since an empty string is not a valid format for Int32.Parse , the attempt is reset.

If the element is completely missing from your XML, this works as you would expect, but if there is an empty <yearnode/> or <yearnode></yearnode> , you will get an exception.

+2
source

You can add Where operator

 ..... .Where(a => ! string.IsNullOrEmpty(a)).ToList(); 
+1
source

If the year is an empty or empty string, in which you will get the exception "The source string is not in the correct format." You can write an extension method to read values. I have not tested the code below, but it can give you some tips.

 public static ReadAs<T>(this XElement el, T defaultValue) { var v = (string)el; // cast string to see if it is empty if (string.IsNullOrEmpty(v)) // test return defaultValue; return (T)el; // recast to our type. } 
+1
source

The message Input string was not in a correct format similar to what int.parse () was selected, so it may be that you have a yearnode with a value (not null), but which cannot be successfully parsed to an integer value.

Something like this might fix:

 List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname") let yearNode = g.Element("yearnode") select new GrantAgresso { Year = string.IsNullOrWhiteSpace(yearNode.Value) ? 0 : int.Parse(yearNode.Value), Subdomain = g.Element("domainnode").Value }).ToList(); 

A few notes:

select new GrantAgresso - you do not need parentheses for the default constructor with object initializers.

string.IsNullOrWhiteSpace - was introduced in .net 4.0, use string.IsNullOrEmpty if you are on 3.5 or earlier versions

g.Element("domainnode").Value - will always return a string

if you need null for Year instead of 0, use (int?)null instead of 0

+1
source

Your problem is that cast from XElement to int? uses the int.Parse method for the string value of XElement, which in your case is String.Empty. The following results show the same error:

 XElement x = new XElement("yearnode", String.Empty); int? a = (int?)x; // throws FormatException 

You can avoid this by first introducing an XElement into the string and checking to see if it is empty or empty, and only if it does not convert to int ?. To do this, replace

 Year = (int?)g.Element("yearnode") ?? 0, 

from

 Year = !string.IsNullOrEmpty((string)g.Element("yearnode")) ? (int?)g.Element("yearnode") : 0, 

This is not very pretty and will still throw if the string is otherwise not legal, but it works if you can assume that the element is always an integer or null / empty.

0
source

All Articles