Search code examples
c#xmllinqnull-coalescing-operator

C# ?? null coalescing operator LINQ


I am trying to prevent having NULL values when I parse an XML file to a custom object using LINQ.

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

Below is an extract 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();

The errormessage is:

Input string was not in a correct format.

If anyone has a clue as to what I'm doing wrong, please help :)

Edit: piece of XML (strange names but it's not by choice)

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

Solution

  • The following extension method will return 0 both if the element is not present, the element is empty or it contains a string that cannot be parsed to 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 could use it like this:

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

    Or if you're ready to consider the cost of reflection and to accept the default value of any value type, you may 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"),
    ...