2012-10-02 20 views
7

Estoy tratando de evitar tener valores NULL cuando analizo un archivo XML en un objeto personalizado utilizando LINQ.C# ?? nulo coalescente operador LINQ

Encontré una gran solución para esto en Scott Gu's blog, pero por alguna razón no funciona para enteros conmigo. Creo que he usado la misma sintaxis, pero parece que me falta algo. Ah, y por alguna razón funciona cuando el nodo no está vacío.

A continuación se muestra un extracto de mi código.

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(); 

El ErrorMessage es:

Input string was not in a correct format.

Si alguien tiene una idea de lo que estoy haciendo mal, por favor ayuda :)

Edición: pieza de XML (nombres extraños, pero es no por elección)

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

Mi apuesta está en la sintaxis XML incorrecta. ¿Puedes mostrar el contenido xml? El operador de fusión es correcto y, si no fuera así, no lanzaría este error (no tiene nada que ver con las cadenas) –

+0

¿La asignación 'Año' o' Subdominio' arroja la excepción? Podría intentar comentar una de las líneas para ver si la excepción desaparece. – Mizipzor

+0

He agregado un nodo del xml a mi publicación. Es el nodo vacío (que comienza con r7_) que desencadena la excepción. El código que he usado para recuperarlo es el mismo que he usado para el año. – Nielsm

Respuesta

1

El siguiente método de extensión devolverá 0, tanto si el elemento no está presente, el elemento está vacío o contiene una cadena que no se puede analizar a un entero:

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

Usted puede utilizar de esta manera:

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

O si usted está listo para considerar el costo de la reflexión y para aceptar el valor por defecto de cualquier tipo de valor, es posible utilizar este método de extensión:

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

y lo utilizan:

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

He utilizado su segunda sugerencia. ¡Gracias! – Nielsm

1

puede añadir Where operator

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

¿Qué se supone que 'a' está aquí? ¿Por qué llamas a 'ToList' en lo que parece un' IEnumerable '? – Rawling

+2

Esto no hace lo que el OP intenta hacer. –

+1

No, no puedo, no quiero filtrar los resultados, simplemente no quiero que mi programa se rompa cuando aparece un nodo vacío ... – Nielsm

1

Si el año es nulo o vacío cadena que obtendrá una "cadena de entrada no estaba en un formato correcto" excepción. Puede escribir un método de extensión para leer valores. No he probado el código a continuación, pero puede darle algunas pistas.

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. 
} 
2

Es esta expresión, que está lanzando:

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

Eso es porque si el valor actual del nodo de texto del elemento es String.Empty y no null, ya que la cadena vacía no es un formato válido para Int32.Parse , el intento de lanzamiento falla.

Si el elemento falta por completo de su XML, esto funciona como esperaba, pero si hay una etiqueta vacía <yearnode/> o <yearnode></yearnode>, obtendrá la excepción.

+0

Esta podría ser la respuesta que estaba buscando. ¿Qué sugieres que haga? – Nielsm

1

El mensaje Input string was not in a correct format se parece a la lanzada por int.parse() así que podría ser que usted tiene una yearnode con un valor (no nulo), pero que no puede ser analizado correctamente a un valor entero.

Algo como esto puede solucionarlo:

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(); 

Un par de cosas para tomar nota:

select new GrantAgresso - usted no necesita paréntesis para un constructor por defecto con inicializadores de objeto.

string.IsNullOrWhiteSpace - se introdujo en .net 4.0, use string.IsNullOrEmpty si tiene 3.5 o anterior

g.Element("domainnode").Value - siempre devuelve una cadena

si quieres un nulo para el año en lugar de 0, utilice (int?)null en lugar de 0

+0

Hola, Acabo de comprobar doble y triple mi XML, los valores que quiero convertir a int son vacíos o un número entero válido. Esto es algo que también he considerado ;-) – Nielsm

+0

He actualizado mi respuesta con una sugerencia para tratar con los vacíos. –

0

Su problema es que el elenco de XElement a int? utiliza el método int.Parse en el valor de cadena de XElement, que en su caso es String.Empty. Los siguientes resultados en el mismo error:

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

Esto se puede evitar si primero la XElement a la cadena y comprobar si es nulo o vacío y sólo si no hacer el reparto a int ?. Para ello, reemplace

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

con

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

No es bonita y seguirá siendo tirar si la cadena es de otra manera no legal, pero funciona si se puede asumir que el elemento es siempre un entero o nulo/vacío