2010-11-28 7 views
3

J'utilise une chaîne d'appels à la méthode XElement.Element() de forer vers le bas dans un document XML et tirer la valeur d'un attribut:Éviter l'exposition à NullReferenceException lorsque vous utilisez LINQ pour XML

XElement root = ...; 
XNamespace ns = "..."; 
var firstName = 
    root 
    .Element(ns + "Employee") 
    .Element(ns + "Identity") 
    .Element(ns + "Name") 
    .Attribute(ns + "FirstName"); 

Cependant, depuis la le document entrant n'a pas été validé par le schéma, il est possible qu'un document malformé provoque un NullReferenceException si l'un des éléments intermédiaires attendus n'existe pas.

Existe-t-il un moyen d'éviter ce risque, tout en gardant le code succinct?

Je peux envelopper le code ci-dessus dans un gestionnaire pour NullReferenceException mais cela ne semble pas correct et n'indiquerait pas non plus spécifiquement où la panne s'est produite. La construction d'un message d'erreur informatif serait manuelle, fastidieuse, sujette aux erreurs et risque de maintenance. Au lieu de cela, est-ce que je devrais utiliser XPath à la place, de cette façon je peux vérifier un retour null, puis construire facilement un message d'erreur indiquant que l'expression XPath n'a pas pu être résolue?

+0

Juste pour assurez-vous, pourquoi le document entrant n'est-il pas validé? – Neowizard

+0

Pouvez-vous utiliser simplement Descendents (ns + "FirstName")? –

Répondre

7

Une option consiste à utiliser Elements() au lieu de Element() - qui donnera une séquence vide si l'élément n'est pas trouvé. En utilisant les méthodes d'extension dans Extensions, vous pouvez passer d'une séquence d'éléments à une séquence d'éléments - et la même chose pour les attributs. Alors:

var firstName = 
    root 
    .Elements(ns + "Employee") 
    .Elements(ns + "Identity") 
    .Elements(ns + "Name") 
    .Attributes(ns + "FirstName") 
    .FirstOrDefault(); 

Il est une différence entre les deux extraits, l'esprit que vous - ce trouverez attribut le premier correspondant, même si elle vient de (par exemple) le premier élément de nom dans le troisième élément d'identité au sein de la seconde Élément employé. Cela peut ou peut ne pas être un problème pour vous.

(Juste pour vérifier, vous avez certainement besoin l'espace de noms sur l'attribut? Contrairement éléments, les attributs ne héritent pas un espace de noms « par défaut ». Il est plus rare d'utiliser des attributs sur les espaces de noms que sur les éléments.)

+0

Excellente suggestion Jon. Maintenant vous avez juste besoin d'une vérification nulle avant d'utiliser le résultat, tout comme pour une évaluation XPath. Par intérêt, préféreriez-vous utiliser cette approche sur une expression XPath? (Et oui, dans ce cas, le schéma auquel je pense a effectivement des attributs namespaced.) –

+0

@Daniel: Personnellement, je ne suis pas très friands de XPath dans le code - voir http://msmvps.com/blogs/jon_skeet/ archive/2010/08/29/code-and-data.aspx pour mon raisonnement. Bien sûr, il y a des endroits où cela a du sens, mais dans ce cas, j'irais probablement avec la solution LINQ to XML. –

1

Vous pouvez définir la méthode en extension pour recouvrir la détection de null. Quelque chose comme ça, par exemple:

public static class XmlExtender 
{ 
    public static XAttribute AttributeOrDefault(this XElement el, XName name, string defValue) 
    { 
     var v = el.Attribute(name); 
     return v == null ? new XAttribute(name, defValue) : v; 
    } 

    public static string AttributeValue(this XElement el, XName name, string defValue) 
    { 
     var v = el.Attribute(name); 
     return v == null ? defValue : v.Value; 
    } 
} 

qui peut alors être utilisé comme ceci:

var firstName = root.ELement("elname") 
        .AttributeOrDefault("attrName", "N/A").Value; 

ou ceci:

var firstName = root.Element("elname") 
        .AttributeValue("attrName", "N/A"); 
Questions connexes