2011-10-26 12 views
5

Quindi ho una classe che si occupa di leggere e conservare il file XML. In questo momento una semplice versione di esso assomiglia a questo:Verificare se XElement è nullo a livello globale

public class EstEIDPersoConfig 
{ 
    public bool LaunchDebugger { get ; set; } 
    public string Password { get; set; } 
    public int Slot { get; set; } 
    public string Reader { get; set; } 
    public string TestInput { get; set; } 
    public bool Logging { get; set; } 

    public EstEIDPersoConfig() 
    { 
     XElement xml = XElement.Load(myxml.xml); 
     XElement Configuration = xml.Element("Configuration"); 

     LaunchDebugger = Convert.ToBoolean(Configuration.Element("LaunchDebugger").Value); 
     Password = Configuration.Element("Password").Value; 
     Slot = Convert.ToInt32(Configuration.Element("Slot").Value); 
     Reader = Configuration.Element("Reader").Value; 
     TestInput = Configuration.Element("TestInput").Value; 
     Logging = Convert.ToBoolean(Configuration.Element("Logging").Value); 
    } 
} 

E non ci sarà più tardi. quindi il problema è che se qualche elemento non esiste in xml ottengo System.NullReferenceException. Quindi ho bisogno di controllare se l'elemento è null o no. Ecco un modo per farlo:

var value = Configuration.Element("LaunchDebugger").Value; 
if (value != null) 
    LaunchDebugger = Convert.ToBoolean(value); 
else 
    throw new Exception("LaunchDebugger element missing from xml!"); 

Ma farlo per ogni elemento sarebbe troppo. Quindi ho bisogno di alcune buone idee su come semplificare questo sistema in modo che non finisca in 1000 righe di codice.

MODIFICA: Modificato l'ultimo snippet di codice, l'idea non era quella di impostare un valore predefinito, l'idea era di notificare all'utente che questo elemento che cosa è nullo manca da xml.

+0

Eseguire la cosiddetta inizializzazione pigra di xml nel costruttore appropriato quando il valore di xml è nullo. – mozillanerd

+1

@ hs2d - Penso che la risposta di abatishchev sia ciò che stai cercando. – Tim

risposta

4

L'idea arriva direttamente da risposta di abatischev in modo che merita il credito.

Come descritto da Microsoft here, è possibile trasmettere il XElement al tipo desiderato.

LaunchDebugger = (bool?)Configuration.Element("LaunchDebugger"); 

se si vuole gestire il caso null Credo che si potrebbe fare

LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? true); 

o forse

LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? false); 

a seconda della logica di business. Se fai lo stesso coalescene per un tipo specifico, potrebbe essere appropriato avvolgere questo liner in un metodo, un'estensione o altro, ma sono incerto che aggiungerebbe molto.

+1

Sembra che sia necessario utilizzare 'bool?' – abatishchev

+0

aggiornato, lo stesso vale per altri primitivi. – Jodrell

+1

+1 per usare il ?? (operatore a coalescenza nulla, credo?) per impostare un valore predefinito nel caso di elementi nulli. Ho imparato due cose utili da questo thread stasera :) – Tim

2

Estrarre la logica in un metodo e avere metodi di overload per la conversione di booleani e altri tipi di dati Int32.

public static void GetElementValue(XElement xElement, string parameter, out bool value) 
    { 
     var stringValue = xElement.Element(parameter).Value; 
     value = false; 
     if (value != null) 
      value = Convert.ToBoolean(stringValue); 
    } 
+0

Non userei un parametro 'out', ma il valore restituito. E sarebbe più utile se la stringa "LaunchDebugger" fosse un parametro. –

+0

Errore di battitura risolto. Non stavo usando IDE per scrivere il codice. – luqi

+0

Ma in questo modo devo ancora creare un metodo esterno per ogni elemento xml? O mi manca qualcosa qui .. – hs2d

1

Come su un metodo esterno:

public static class XElementExtensions 
{ 
    public static bool AsBoolean(this XElement self, bool defaultValue) 
    { 
     if (self == null) 
     { 
      return defaultValue; 
     } 
     if (!string.IsNullOrEmpty(self.Value)) 
     {   
      try 
      { 
       return XmlConvert.ToBoolean(self.Value); 
      } 
      catch 
      { 
       return defaultValue; 
      } 
     } 
     return defaultValue; 
    } 
} 

Ho provato questo con SnippetCompiler:

XElement test = new XElement("test", 
    new XElement("child1"), 
    new XElement("child2", new XText("true")), 
    new XElement("child3", new XText("false")), 
    new XElement("child4", new XText("rubbish"))); 

WL(test.Element("child1").AsBoolean(false)); // note, "child1" has no value (or is `""`) 
WL(test.Element("child2").AsBoolean(false)); 
WL(test.Element("child3").AsBoolean(false)); 
WL(test.Element("child4").AsBoolean(false)); 
WL(test.Element("child5").AsBoolean(false)); // note, "child5" doesn't exist   

per produrre questo risultato:

False 
True 
False 
False 
False 

aggiungere più come metodi per altri tipi e aggiungere anche AsBoolean(defaultValue), in quanto può tornare utile quando si desidera eseguire l'impostazione predefinita su true!

Come altri hanno affermato, è possibile utilizzare l'operatore ?? per fornire un valore per null. Questo non lo fa nido, però, così:

LaunchDebugger = XmlConvert.ToBoolean(Configuration.Element("LaunchDebugger").Value) ?? false; 

volontà attraverso un NullReferenceException se non v'è tale elemento nel file XML.

+2

Non sarebbe semplice usare l'approccio microsoft perscribed nella risposta di abatischev con l'operatore di coalesenza '?? – Jodrell

1

È possibile definire un metodo per estrarre il valore per voi e fare qualche controllo su null lì. Quindi avvolgere il recupero del valore in un proprio metodo in questo modo:

public string GetXMLValue(XElement config, string elementName){ 
    var element = Configuration.Element(elementName); 

    if(element == null) 
     return String.Empty; 

    return element.Value; 
} 

Naturalmente è possibile estendere questo per funzionare correttamente con l'analisi di boolean ecc

+3

Perché gli sviluppatori di framework non hanno aggiunto questo? Risposta, perché non è necessaria. Vedi la risposta di abatischev. – Jodrell

+1

@Jodrell: Lo vedo ora .... Penso di aver davvero imparato qualcosa da questo post;) Grazie amico! –

2

Ho un metodo di estensione che uso solo per questo tipo di cosa:

public static T GetValue<T>(
      this XElement @this, 
      XName name, 
      Func<XElement, T> cast, 
      Func<T> @default) 
    { 
     var e = @this.Element(name); 
     return (e != null) ? cast(e) : @default(); 
    } 

Ti dà il casting richiesto e anche un valore predefinito di fabbrica.

Ecco come ci si utilizza:

LaunchDebugger = Configuration.GetValue("LaunchDebugger", 
    x => Convert.ToBoolean(x),() => false); 
Password = Configuration.GetValue("CMKPassword", x => (string)x,() => ""); 
Slot = Configuration.GetValue("CMKSlot", x => (int)x,() => -1); 
Reader = Configuration.GetValue("Reader", x => (string)x,() => ""); 
TestInput = Configuration.GetValue("TestInput", x => (string)x,() => ""); 
Logging = Configuration.GetValue("Logging", 
    x => Convert.ToBoolean(x),() => false); 
+1

Non riesco a vedere che passare due lambda ad un generico è meglio che solo lanciare e usare l'operatore di coalesenza "??" null – Jodrell

+0

Perché il secondo lambda invece di un semplice valore di tipo "T"? –

+0

@Jodrell - La 'cast' cast' è pensata meglio come una conversione. Lo faccio in questo modo per rendere il codice coerente. – Enigmativity

4
(bool)Configuration.Element("LaunchDebugger") 

o

(bool?)Configuration.Element("LaunchDebugger") 

non dovrebbe lanciare un'eccezione.

See MSDN:

+0

Perché il downvote, mi sembra la migliore risposta a me http://msdn.microsoft.com/en-us/library/bb387049.aspx – Jodrell

+0

@Jodrell: Sembra che tu abbia ragione ... il mio male. Sembra un po 'strano però! –

+0

Non dovrebbe essere '(bool?) Configuration.Element (" LaunchDebugger ")'? Ad esempio, rendere bool un tipo nullable. – Tim

Problemi correlati