2010-03-30 10 views
9

Qui è la mia classe:Come deserializzare l'array nullo su null in C#?

public class Command 
{ 
    [XmlArray(IsNullable = true)] 
    public List<Parameter> To { get; set; } 
} 

Quando ho serializzare un oggetto di questa classe:

var s = new XmlSerializer(typeof(Command)); 
s.Serialize(Console.Out, new Command()); 

viene stampato come (sono omessi gli spazi dei nomi di intestazione XML e di default MS) previsto:

<Command><To xsi:nil="true" /></Command> 

Quando ho preso questo xml e ho provato a deserializzarlo, mi sono staccato perché stampa sempre "Non null":

var t = s.Deserialize(...); 
if (t.To == null) 
    Console.WriteLine("Null"); 
else 
    Console.WriteLine("Not null"); 

Come forzare il deserializzatore per rendere nullo il mio elenco, se è nullo in xml?

+0

È davvero così importante per voi se non avete elenco o una lista vuota? –

+0

Sì. Posso ovviare a questo caso con alcune bandiere, ovviamente, ma voglio sapere se esiste una buona soluzione standard. –

+1

Evitare oggetti nulli di solito è una buona cosa (meno incline agli errori, non è necessario verificare la presenza di null su tutto il codice ecc.). L'uso di una bandiera indicherebbe chiaramente che cosa rappresenta esattamente quel valore nullo. – Groo

risposta

2

Ugh, non è fastidioso esso. È possibile vederlo eseguendo sgen.exe sull'assieme con le opzioni/keep e/debug in modo da poter eseguire il debug del codice di deserializzazione. Sembra più o meno in questo modo:

global::Command o; 
o = new global::Command(); 
if ((object)([email protected]) == null) [email protected] = new global::System.Collections.Generic.List<global::Parameter>(); 
global::System.Collections.Generic.List<global::Parameter> a_0 = (global::System.Collections.Generic.List<global::Parameter>)[email protected]; 
// code elided 
//... 
while (Reader.NodeType != System.Xml.XmlNodeType.EndElement && Reader.NodeType != System.Xml.XmlNodeType.None) { 
    if (Reader.NodeType == System.Xml.XmlNodeType.Element) { 
    if (((object)Reader.LocalName == (object)id4_To && (object)Reader.NamespaceURI == (object)id2_Item)) { 
     if (!ReadNull()) { 
     if ((object)([email protected]) == null) [email protected] = new global::System.Collections.Generic.List<global::Parameter>(); 
     global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)[email protected]; 
     // code elided 
     //... 
     } 
     else { 
     // Problem here: 
     if ((object)([email protected]) == null) [email protected] = new global::System.Collections.Generic.List<global::Parameter>(); 
     global::System.Collections.Generic.List<global::Parameter> a_0_0 = (global::System.Collections.Generic.List<global::Parameter>)[email protected]; 
     } 
    } 
    } 
    Reader.MoveToContent(); 
    CheckReaderCount(ref whileIterations1, ref readerCount1); 
} 
ReadEndElement(); 
return o; 

Non meno di 3 posti in cui si assicura la proprietà @ su non è nullo. Il primo è in qualche modo difendibile, difficile da deserializzare i dati quando la struttura non esiste. Il secondo è il test nullo di nuovo, quello è l'unico vero bene. Il terzo è il problema, ReadNull() ha restituito true ma è ancora crea un valore di proprietà non null.

Se si desidera distinguere tra vuoto e null, non si ha una soluzione valida, ma modificare questo codice manualmente. Fai questo solo se sei davvero disperato e la classe è stabile al 100%. Bene, non farlo. La soluzione di João è l'unica buona.

+0

Grazie. Aggiungerò le bandiere. –

0

Sono d'accordo con il commento di @ Oliver, ma è possibile risolverlo in questo modo se è assolutamente necessario restituire null. Invece di usare una proprietà automatica, crea il tuo campo di supporto.

List<Parameter> _to; 
public List<Parameter> To 
{ 
    get 
    { 
     if (_to != null && _to.Count == 0) return null; 
     return _to; 
    } 
    set { _to = value; } 
} 
+0

Credo che l'OP abbia voluto * differenziare * liste vuote e vuote. – Groo

+0

Sfortunatamente, nel mio caso, la lista vuota e la lista nulla sono differenti. –

+0

Questo rende più complicato subito :( –

0

Se avete veramente bisogno che una collezione è deserializzata a null quando non vengono forniti valori si può fare non fornendo una funzione di accesso set, in questo modo:

public class Command 
{ 
    private List<Parameter> to; 

    public List<Parameter> To { get { return this.to; } } 
} 
+0

Cattiva scelta. "A" sarà sempre nullo dopo la deserializzazione –

3

Se si utilizza una matrice invece di una lista che funziona come previsto

public class Command 
{ 
    [XmlArray(IsNullable = true)] 
    public Parameter[] To { get; set; } 
} 
Problemi correlati