2017-02-07 3 views
1

J'ai deux classes, "société" dérivé de Treenode, et "document".Sérialiser/désérialiser XML dérivé de Treenode

[Serializable] 
[XmlRoot("Company")] 
public class Company : TreeNode, IXmlSerializable 
{ 
    private string _x; 
    private string _y; 

    public Company() { } 

    [XmlElement("X")] 
    public string X { get; set; } 
    [XmlElement("Y")] 
    public string Y { get; set; } 


    public System.Xml.Schema.XmlSchema GetSchema() 
    { 
     return null; 
    } 
    public void ReadXml(XmlReader reader) 
    { 
     if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Company") 
     { 
      x = reader["X"].ToString; 
      y = reader["Y"].ToString;   
     } 
    } 
    public void WriteXml(XmlWriter writer) 
    { 
     writer.WriteElementString("X", this.X.ToString()); 
     writer.WriteElementString("Y", this.Y.ToString()); 
    } 
} 

public class Document 
{ 
    private int _id; 
    private string _name; 
    private Company _company; 

    public Document() { } 

    [XmlElement("ID")] 
    public int ID { get; set; } 
    [XmlElement("Name")] 
    public string Name { get; set; } 
    [XmlElement("Company")] 
    public Company Comp { get; set; } 
} 

Lorsque j'essaie de sérialiser un document, puis enregistrez dans un fichier, son travail. Mais quand je désérialise, le paramètre du lecteur est toujours nul. Des solutions?

Ceci est mon code pour désérialiser. "sr" est une variable qui contient du texte xml.

var sr = new StreamReader(ms); 
var myStr = sr.ReadToEnd(); 

XmlSerializer serializer = new XmlSerializer(typeof(List<Document>)); 
using (TextReader tr = new StringReader(myStr)) 
{ 
    List<Document> docu = (List<Document>)serializer.Deserialize(tr); 
} 

J'essaie de mettre en œuvre ISerialization et le débogage, mais jamais tiré, et essayer de overide sérialisation et de la méthode deserialize, et pas de chance.

J'utilise .NET Framework 3.5

+0

Avez-vous vérifié votre XML résultant? – Fildor

+0

@Fildor Oui, j'ai vérifié. Lors de la désérialisation, les données de l'entreprise ne se remplissent jamais. – Blishton

+0

Essayez de supprimer IXmlSerializable et les méthodes GetSchema, ReadXml et WriteXML dans la classe Company. Puis réessayez. – Fildor

Répondre

2

Comme expliqué dans this article, la mise en œuvre IXmlSerializable est bien en fait assez difficile. Votre mise en œuvre de ReadXml() semble violer l'exigence que consommer l'élément d'emballage lui-même, ainsi que tout le contenu, comme ceci:

public void ReadXml(System.Xml.XmlReader reader) 
{ 
    reader.MoveToContent(); 
    // Read attributes 
    Boolean isEmptyElement = reader.IsEmptyElement; // (1) 
    reader.ReadStartElement(); 
    if (!isEmptyElement) // (1) 
    { 
     // Read Child elements X and Y 

     // Consume the end of the wrapper element 
     reader.ReadEndElement(); 
    } 
} 

En outre, reader["X"] renvoie la valeur de l'attribut XML nommé "X", comme expliqué dans le docs. Dans votre WriteXml() vous avez écrit les valeurs de X et Y comme imbriqués éléments XML. Ceci explique le NullReferenceException. Vous devez corriger vos méthodes de lecture et d'écriture pour être cohérent.

Cependant, je vous suggère une alternative à la mise en œuvre IXmlSerializable, ce qui est d'introduire un de type substitut pour votre Company. Tout d'abord, extract toutes les propriétés non TreeNode de Company dans une interface:

public interface ICompany 
{ 
    string X { get; set; } 
    string Y { get; set; } 
} 

public class Company : TreeNode, ICompany 
{ 
    public Company() { } 

    public string X { get; set; } 
    public string Y { get; set; } 
} 

Cette option est facultative, mais rend le code plus clair. Ensuite, introduire une POCO de substitution qui implémente la même interface mais ne hérite pas de TreeNode:

public class CompanySurrogate : ICompany 
{ 
    public string X { get; set; } 
    public string Y { get; set; } 

    public static implicit operator CompanySurrogate(Company company) 
    { 
     if (company == null) 
      return null; 
     // For more complex types, use AutoMapper 
     return new CompanySurrogate { X = company.X, Y = company.Y }; 
    } 

    public static implicit operator Company(CompanySurrogate surrogate) 
    { 
     if (surrogate == null) 
      return null; 
     // For more complex types, use AutoMapper 
     return new Company { X = surrogate.X, Y = surrogate.Y }; 
    } 
} 

Notez que la mère porteuse peut être converti implicitement à votre type Company original? Maintenant, vous pouvez utiliser la mère porteuse dans sérialisation XML en définissant la propriété d'attribut XmlElementAttribute.Type être celui de la mère porteuse:

public class Document 
{ 
    public Document() { } 

    [XmlElement("ID")] 
    public int ID { get; set; } 
    [XmlElement("Name")] 
    public string Name { get; set; } 

    [XmlElement("Company", Type = typeof(CompanySurrogate))] 
    public Company Comp { get; set; } 
} 

On évite ainsi toutes les possibilités d'erreur dans la mise en œuvre IXmlSerializable. Compte tenu de la liste d'entrée suivante:

var list = new List<Document> 
{ 
    new Document { Name = "my name", ID = 101, Comp = new Company { X = "foo", Y = "bar", NodeFont = new System.Drawing.Font("Arial", 10) } }, 
    new Document { Name = "2nd name", ID = 222, Comp = new Company { X = "tlon", Y = "ukbar" } }, 
}; 

Le code XML suivant peut sera généré et peut être désérialisé avec succès:

<ArrayOfDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Document> 
     <ID>101</ID> 
     <Name>my name</Name> 
     <Company> 
      <X>foo</X> 
      <Y>bar</Y> 
     </Company> 
    </Document> 
    <Document> 
     <ID>222</ID> 
     <Name>2nd name</Name> 
     <Company> 
      <X>tlon</X> 
      <Y>ukbar</Y> 
     </Company> 
    </Document> 
</ArrayOfDocument> 

Cela dit, je ne recommande pas vraiment cette conception. Votre interface utilisateur doit présenter votre modèle de données, il ne doit pas être votre modèle de données. Voir par exemple How does one implement UI independent applications?.Remplacer Company par ICompany autant que possible pourrait être une première étape pour changer votre conception; vous pouvez trouver qu'il devient plus facile de remplacer votre architecture existante par un TreeNode qui ressemble à ceci:

public class CompanyNode : TreeNode 
{ 
    public ICompany Company { get; set; } 
}