Domanda

Sto cercando di evitare di avere valori nulli quando analizzo un file XML a un oggetto personalizzato utilizzando LINQ.

Ho trovato una grande soluzione per questo su Scott Gue's Blog , ma per qualche motivo non funziona per i numeri interi con me.Penso di aver usato la stessa sintassi ma sembra che mi manchi qualcosa.Oh e per qualche ragione funziona quando il nodo non è vuoto.

di seguito è un estratto del mio codice.

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

L'errormessage è:

.

La stringa di ingresso non era in un formato corretto.

Se qualcuno ha un indizio su ciò che sto facendo male, per favore aiuto :)

Modifica: pezzo di XML (nomi strani ma non è a scelta)

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

È stato utile?

Soluzione

Il seguente metodo di estensione restituirà 0 entrambi se l'elemento non è presente, l'elemento è vuoto o contiene una stringa che non può essere analizzata su numero intero:

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

Potresti usarlo come questo:

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

o se sei pronto a considerare il costo della riflessione e di accettare il valore predefinito di qualsiasi tipo di valore, è possibile utilizzare questo metodo di estensione:

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

e usalo:

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

Altri suggerimenti

È questa espressione che sta lanciando:

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

Questo perché se il valore effettivo del nodo di testo dell'elemento è String.Empty e non null, poiché la stringa vuota non è un formato valido per Int32.Parse, il cast tentato non riesce.

Se l'elemento manca completamente dal tuo XML, funziona come ti aspetti, ma se c'è un tag vuoto <yearnode/> o <yearnode></yearnode>, avrai l'eccezione.

Puoi aggiungere Where operator

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

Se l'anno è null o Stringa vuota che ottiene una "stringa di ingresso non era in un formato corretto" eccezione.È possibile scrivere un metodo di estensione per leggere i valori.Non ho testato il codice qui sotto, ma potrebbe darti alcuni suggerimenti.

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

Il messaggio Input string was not in a correct format sembra quello lanciato da int.parse () in modo che tu possa essere che tu possa avere un yearnode con un valore (non nullo) ma che non può essere analizzato correttamente a un valore intero.

Qualcosa Ti piace questo potrebbe risolverlo:

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 paio di cose da notare:

select new GrantAgresso: non è necessario parentesi per un costruttore predefinito con inizializzatori oggetto.

string.IsNullOrWhiteSpace - è stato introdotto in .NET 4.0, utilizzare string.IsNullOrEmpty se sei su 3.5 o in precedenza

g.Element("domainnode").Value - restituirà sempre una stringa

Se si desidera un NULL per l'anno anziché 0, utilizzare (int?)null anziché 0

Il tuo problema è che il cast da Xelement a INT?Utilizza il metodo Int.Parse sul valore stringa di Xelement, che nel tuo caso è String.empty.I seguenti risultati nello stesso errore:

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

È possibile evitare questo lanciare prima lo Xelement alla stringa e controllando se è nullo o vuoto e solo se non è il cast per int?.Per fare questo, sostituire

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

con

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

Non è carino e poi lanciare se la stringa non è altrimenti legale, ma funziona se si può supporre che l'elemento sia sempre un intero o NULL / vuoto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top