2015-10-07 1 views
0

J'ai un fichier XML avec un certain nombre de Unit s:Comment puis-je lire un élément XML complet ici?

<Unit Name="Length"> 
<Prefix Char="c" 
      IsSelected="false" 
      Factor="1"/> 

<Prefix Char="d" 
      IsSelected="true" 
      Factor="104"/> 
</Unit> 

Je veux lire un objet entier:

public static Dictionary<string, Unit> Units { get; set; } 
public class Prefix 
{ 
    public Func<double, double> fnc = null;    
    public Prefix(string c, double f, bool i, bool ifix = false,string fn = null) 
    { 
     Char = c; 
     Factor = f; 
     IsFixed = ifix; 
     Unit.funcs.TryGetValue(fn, out fnc); 
    } 

    public bool IsSelected { get; set; } 
    public bool IsFixed { get; set; } 
    public double Factor { get; set; } 
    public string Char { get; set; } 

} 

public Unit() { } 
public Unit(string n, List<Prefix> p) 
{ 
    _name = n; 
    Prefixes = p; 
} 

private List<Prefix> _prefixes; 
public List<Prefix> Prefixes 
{ 
    get { return _prefixes; } 
    set { _prefixes = value; } 
} 
private string _name; 
public string Name 
{ 
    get { return _name; } 
    set { _name = value; } 
} 
    .... 
} 

J'ai maintenant ceci:

Form.Units = (data.Descendants("Unit").Select(x => new Unit 
        (
         x.Attribute("Name").Value, 
         (List<Prefix>) x.Descendants("Prefix").Select(p => new Prefix(
          p.Attribute("Char").Value, 
          Convert.ToDouble(p.Attribute("Factor").Value), 
          p.Attribute("IsSelected").Value == "true", 
          p.Attribute("IsFixed").Value == "true", 
          p.Attribute("Func").Value) 
         ) 
        ) 
        ) 
       ).ToDictionary(x => x.Name, x => x); 

et obtenir l'erreur suivante:

"Unable to cast object of type 'WhereSelectEnumerableIterator 2[System.Xml.Linq.XElement,DD.Prefix]' to type 'System.Collections.Generic.List 1[DD.Prefix]'."

Apparemment, il y a quelque chose de mal avec le (List<Prefix>)

Qu'est-ce que la requête devra être alors? Comment puis-je obtenir des choses dans la liste <>?

.

+1

Il serait plus simple d'appeler '.ToList()' sur votre sous-requête. Comme ceci: 'x.Descendants (" Préfixe "). Sélectionnez (p => nouveau préfixe (....). ToList()' – Tim

+0

Notez également que vous pouvez obtenir des exceptions de référence NULL lorsque les attributs (ou éléments) ne sont pas Vous pouvez éviter cela en utilisant un transtypage explicite dans 'string' (qui retournera null si l'élément/l'attribut est introuvable), par exemple' (chaîne) p.Attribute ("Char") 'ou' Convert.ToDouble ((chaîne) p.Attribute ("Factor")) '. – Tim

Répondre

1

Je voudrais exprimer ce code de la manière suivante .. car il évite de faire des constructeurs de paramètres qui contraindraient ces types à des expressions IEnuermable. ie éviter de faire des constructeurs de paramètres sur les types que vous prévoyez d'utiliser pour l'interrogation ..

Je place les types qui utilisent dans/par Decendents comme XmlElement type ... mais je suis sûr que c'est inexact .. il suffit de le remplacer avec tout ce qui est le bon type.

En outre, cet extrait ne prend pas en compte Unit.funcs.TryGetValue(fn, out fnc); .. et il suppose qu'il existe le nom de propriété Func sur le type Prefix. Vous pouvez effectuer des vérifications nuls pendant l'assignation/le réglage.

data.Descendants("Unit") 
       .Select<XmlElement, Unit>(x => new Unit() 
       { 
        Name = x.Attribute("Name").Value, 
        Prefixes = x.Descendants("Prefix").Select<XmlElement, Prefix>(p => new Prefix() 
        { 
         Char = p.Attribute("Char").Value, 
         Factor = Convert.ToDouble(p.Attribute("Factor").Value), 
         IsSelectd = p.Attribute("IsSelected").Value == "true", 
         IsFixed = p.Attribute("IsFixed").Value == "true", 
         Func = p.Attribute("Func").Value 
        }).ToList() 
       }) 
       .Select<Unit, KeyValuePair<string, Unit>>(unit => new KeyValuePair<string, Unit>() 
       { 
        Key = x.Name, 
        Value = x 
       }) 
       .ToList() 
       .ForEach(kvp => { 
        Form.Units.Add(kvp.Key, kvp.Value); 
       }); 
+0

'.Descendants renvoie une * collection * de 'XElement', cette collection contenant tous les éléments enfants qui correspondent au sélecteur. De plus, en fonction du code XML d'OP, votre requête lancera une exception de référence nulle sur les attributs qui ne sont pas présents dans l'objet 'XElement'. – Tim

1

Ce n'est pas une liste comme cela est, mais une requête, de sorte que vous ne pouvez pas le jeter à une liste, mais vous pouvez appeler ToList sur elle pour l'avoir énuméré et une liste renvoyée:

Form.Units = (data.Descendants("Unit").Select(x => new Unit 
       (
        x.Attribute("Name").Value, 
        //(List<Prefix>) no casting needed 
        x.Descendants("Prefix").Select(p => new Prefix(
         p.Attribute("Char").Value, 
         Convert.ToDouble(p.Attribute("Factor").Value), 
         p.Attribute("IsSelected").Value == "true", 
         p.Attribute("IsFixed").Value == "true", 
         p.Attribute("Func").Value) 
        ).ToList() // you had an IEnumerable<Prefix> now this is a List<Prefix> 
       ) 
       ) 
      ).ToDictionary(x => x.Name, x => x); 
1

Pour élargir sur mes commentaires, il y a quelques choses ici. Tout d'abord, vous pouvez utiliser la méthode d'extension .ToList() sur le .Select pour convertir la collection IEnumerable<T> en List<T>.

Deuxièmement, vous obtiendrez une exception de référence NULL si des attributs ou des éléments sont manquants dans la requête. Vous pouvez gérer cela en toute sécurité en explicitement la conversion en string (et en convertissant le résultat si nécessaire).

La requête mise à jour ressemblerait à ceci:

Form.Units = (data.Descendants("Unit").Select(x => new Unit 
    ((string)x.Attribute("Name"), 
    x.Descendants("Prefix").Select(p => new Prefix(
     (string)p.Attribute("Char"), 
     Convert.ToDouble((string)p.Attribute("Factor")), 
     (string)p.Attribute("IsSelected") == "true", 
     (string)p.Attribute("IsFixed") == "true", 
     (string)p.Attribute("Func")).ToList() 
    ) 
    ) 
) 
).ToDictionary(x => x.Name, x => x); 

Notez que vous n'avez pas besoin .Value lors de l'utilisation (string) (depuis .Value est déjà string).