2009-08-13 4 views
4

XmlRoot ne semble pas fonctionner avec les classes contenues dans une collection. Voici les classes je définis:Xml Sérialisation dans une collection

[XmlRoot("cars")] 
public class CarCollection : Collection<Car> 
{ 
} 

[XmlRoot("car")] 
public class Car 
{ 
    [XmlAttribute("make")] 
    public String Make { get; set; } 

    [XmlAttribute("model")] 
    public String Model { get; set; } 
} 

Voici le code que je utilise pour sérialiser ces objets:

CarCollection cars = new CarCollection(); 
    cars.Add(new Car { Make = "Ford", Model = "Mustang" }); 
    cars.Add(new Car { Make = "Honda", Model = "Accord" }); 
    cars.Add(new Car { Make = "Toyota", Model = "Tundra" }); 

    using (MemoryStream memoryStream = new MemoryStream()) 
    { 
    XmlSerializer carSerializer = new XmlSerializer(typeof(CarCollection)); 
    carSerializer.Serialize(memoryStream, cars); 
    memoryStream.Position = 0; 

    String xml = null; 
    using (StreamReader reader = new StreamReader(memoryStream)) 
    { 
     xml = reader.ReadToEnd(); 
     reader.Close(); 
    } 
    memoryStream.Close(); 
    } 

Le xml après sérialisation ressemble à ceci:

<cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Car make="Ford" model="Mustang" /> 
    <Car make="Honda" model="Accord" /> 
    <Car make="Toyota" model="Tundra" /> 
</cars> 

Avis le "C" dans la voiture n'est pas en minuscules. Qu'est-ce que je dois changer pour que cela se produise? Si je sérialise la voiture directement il sort comme je l'attendrais.

MISE À JOUR: J'ai trouvé une autre solution de contournement. Je ne suis pas sûr à quel point je l'aime mais ça marchera pour mon cas. Si je crée une classe personnalisée (voir ci-dessous) et que CarCollection en dérive, la sérialisation fonctionne comme prévu.

public class XmlSerializableCollection<T> : Collection<T>, IXmlSerializable 
    { 
    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     bool wasEmpty = reader.IsEmptyElement; 
     reader.Read(); 
     if (wasEmpty) 
     { 
     return; 
     } 

     XmlSerializer serializer = new XmlSerializer(typeof(T)); 

     while (reader.NodeType != XmlNodeType.EndElement) 
     { 
     T t = (T)serializer.Deserialize(reader); 
     this.Add(t); 
     } 

     if (reader.NodeType == XmlNodeType.EndElement) 
     { 
     reader.ReadEndElement(); 
     } 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     XmlSerializer reqSerializer = new XmlSerializer(typeof(T)); 
     foreach (T t in this.Items) 
     { 
     reqSerializer.Serialize(writer, t); 
     } 
    } 
    } 
+0

Savez-vous pourquoi la solution de contournement d'insérer une classe XmlSerializableCollection personnalisée provoque le xml que vous voulez être généré pendant la sérialisation? – jameswelle

+0

Cela fonctionne car je sérialise manuellement les objets enfants et respecte l'attribut XMLRoot sur l'enfant. –

Répondre

1

[XmlType("car")] 
public class Car 
{ 

} 
1

Peut-être est un flic-out, mais j'ai pu faire ce travail en utilisant la DataContractSerializer comme ceci:

using System; 
using System.IO; 
using System.Collections.ObjectModel; 
using System.Runtime.Serialization; 

class Program 
{ 
    static void Main() 
    { 
     CarCollection cars = new CarCollection(); 
     cars.Add(new Car { Make = "Ford", Model = "Mustang" }); 
     cars.Add(new Car { Make = "Honda", Model = "Accord" }); 
     cars.Add(new Car { Make = "Toyota", Model = "Tundra" }); 

     using (MemoryStream memoryStream = new MemoryStream()) 
     { 
      DataContractSerializer serializer 
       = new DataContractSerializer(typeof(CarCollection)); 
      serializer.WriteObject(memoryStream, cars); 
      memoryStream.Position = 0; 

      String xml = null; 
      using (StreamReader reader = new StreamReader(memoryStream)) 
      { 
       xml = reader.ReadToEnd(); 
       reader.Close(); 
      } 
      memoryStream.Close(); 
     } 
    } 
} 

[CollectionDataContract(Name = "cars")] 
public class CarCollection : Collection<Car> { } 

[DataContract(Name = "car")] 
public class Car 
{ 
    [DataMember(Name = "make")] 
    public String Make { get; set; } 

    [DataMember(Name = "model")] 
    public String Model { get; set; } 
} 

Sortie:

<cars xmlns="http://schemas.datacontract.org/2004/07/" 
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
    <car> 
    <make>Ford</make> 
    <model>Mustang</model> 
    </car> 
    <car> 
    <make>Honda</make> 
    <model>Accord</model> 
    </car> 
    <car> 
    <make>Toyota</make> 
    <model>Tundra</model> 
    </car> 
</cars> 

Notez que les attributs sur vos types ont changé pour soutenir l'utilisation de DataContractSerializer. Je ne sais pas si c'est la direction que vous voulez aller avec cela, mais j'ai trouvé que dans presque tous les cas, je préfère utiliser le DataContractSerializer à la place de l'ancien XmlSerializer.

+0

Cela pourrait fonctionner. J'aurais juste besoin de faire en sorte que les propriétés make et model soient des attributs à la place des nœuds enfants. –

+0

Mon erreur - Je vais modifier pour corriger ... –

2

XmlRootAttribute s'applique uniquement si l'élément est la racine du graphe d'objet en cours de sérialisation, c'est-à-dire. l'objet que vous passez à l'instance XmlSerializer.

Pour contrôler la sérialisation des collections, vous devez normalement utiliser le XmlElementAttribute pour spécifier le nom de l'élément avec lequel sérialiser les objets enfants. Malheureusement, cet attribut ne peut être appliqué qu'à un champ ou une propriété et non à une classe.

Si vous pouvez exposer votre collection comme une propriété d'une classe, vous pouvez utiliser l'attribut comme suit:

[XmlRoot("cars")] 
public class CarList 
{ 
    [XmlElement("car")] 
    public CarCollection Cars { get; set; } 
} 

Avec votre exemple de code, produit le résultat suivant:

<cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <car make="Ford" model="Mustang" /> 
    <car make="Honda" model="Accord" /> 
    <car make="Toyota" model="Tundra" /> 
</cars> 

Il est un peu de work-around, mais c'est le plus proche, vous pouvez obtenir sans beaucoup de code personnalisé. Ne pouvez-vous pas simplement baliser la voiture avec XmlType?

Questions connexes