none
[Xml] Sérialisation de classe dérivée RRS feed

  • Question

  • Bonjour,

    je viens d'essayer l'exemple de Microsoft :

    https://msdn.microsoft.com/en-us/library/3z3z5s6h(v=vs.110).aspx

    Ensuite, j'ai voulu adapter leur code pour placer leur tableau en privé et passer par accesseur :

    public class Orders
        {
            private Book[] Books;
    
            public Book[] BookArray
            {
                get { return this.Books; }
                set { this.Books = value; }
            }
        }

    Cela génère une exception "Erreur lors de la génération du document XML."

    (le but est d'avoir une Collection<BaseType> en attribut private avec accesseur ensuite. le BaseType sera dans une DLL et les types dérivés dans l'application en cours).

    Quelle va être la meilleure implémentation ?

    Merci,

    Vincent

    mardi 19 mai 2015 19:02

Réponses

  • J'ai résolu une partie du problème grâce à cet article :

    www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly?msg=5062225#xx5062225xx

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    
    namespace Test
    {
        public class Animal : IXmlSerializable
        {
            public Animal() { }
            public Animal(string name) { Name = name; }
            public String Name { get; set; }
            public DateTime Birthday { get; set; }
    
            public System.Xml.Schema.XmlSchema GetSchema() { return null; }
    
            public void ReadXml(System.Xml.XmlReader reader)
            {
                if (reader == null)  throw new ArgumentNullException("reader");
    
                reader.MoveToContent();
                Name = reader.GetAttribute("Name");
                Boolean isEmptyElement = reader.IsEmptyElement; // (1)
                reader.ReadStartElement();
                if (!isEmptyElement) // (1)
                {
                    Birthday = DateTime.ParseExact(reader.ReadElementString("Birthday"), "yyyy-MM-dd", null);
                    reader.ReadEndElement();
                }
            }
    
            public void WriteXml(System.Xml.XmlWriter writer)
            {
                if (writer == null) throw new ArgumentNullException("writer");
    
                writer.WriteAttributeString("Name", Name);
                if (Birthday != DateTime.MinValue)
                {
                    writer.WriteElementString("Birthday", Birthday.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
                }
            }
        }
        public class Dog : Animal
        {
            public Dog() { }
            public Dog(string name) : base (name) { }
        }
        public class Cat : Animal
        {
            public Cat() { }
            public Cat(string name) : base(name) { }
        }
        public class Mouse : Animal
        {
            public Mouse() { }
            public Mouse(string name) : base (name) { }
        }
    
        public class AnimalList
        {
            private Collection<Animal> listOfAnimals;
    
            public AnimalList() { listOfAnimals = new Collection<Animal>(); }
    
            [XmlElement("Dog", typeof(Dog))]
            [XmlElement("Cat", typeof(Cat))]
            [XmlElement("Mouse", typeof(Mouse))]
            public Collection<Animal> List
            {
                get { return listOfAnimals; } 
            }
        }
    
        public class Farm
        {
            public Farm() { Animals = new AnimalList(); }
    
            public AnimalList Animals { get; set; }
        }
    
    
        class Program
        {
            static void Main()
            {
                try
                {
                    Farm farm = new Farm();
                    string filename = @"..\..\farm.xml";
    
    
                    farm.Animals.List.Add(new Cat("Ati"));
                    farm.Animals.List.Add(new Cat("Gribouille"));
                    farm.Animals.List.Add(new Dog("Joker"));
                    farm.Animals.List.Add(new Cat("Joyeuse"));
                    
                    // Writing the file requires a TextWriter instance.
                    using (TextWriter writer = new StreamWriter(filename))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(Farm));
                        serializer.Serialize(writer, farm);
                    }
    
                    // Read XML file
                    using (TextReader reader = new StreamReader(filename))
                    {
                        XmlSerializer serializer2 = new XmlSerializer(typeof(Farm));
                        Farm farm2 = (Farm)serializer2.Deserialize(reader);
                        Console.WriteLine("Numer of animals : {0}", farm2.Animals.List.Count);
                        foreach (Animal animal in farm2.Animals.List)
                        {
                            Console.WriteLine("Name : {0}", animal.Name);
                        }
                        Console.WriteLine("Press any key to continue...");
                        Console.ReadKey();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        }
    }
    

    ce qui produit le XML :

    <?xml version="1.0" encoding="utf-8"?>
    <Farm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Animals>
        <Cat Name="Ati" />
        <Cat Name="Gribouille" />
        <Dog Name="Joker" />
        <Cat Name="Joyeuse" />
      </Animals>
    </Farm>

    Pour le moment, ça impose de connaître les classes dérivées mais c'est un début.

    Vincent

    jeudi 21 mai 2015 16:12

Toutes les réponses

  • Bonsoir,

    La classe doit être déclarée comme suit:

    [Serializable]
    public class Client
    {
       string m_Client
       public string Client { get { return m_Client; } set { m_Client = value; } }

       int m_ID;
       [NotSerializable]
       public int ID { get { return m_ID; } set { m_ID = value; } }

    }

    Uniquement les propriétés publiques sont 'sérialisées'.

    Et vous pouvez interdire l'exportation de certains champs.

    Je pense également qu'il n'est pas possible d'exporter des dictionnaires...

    Les listes, cela doit passer. A vérifier.

    Cordialement,
    Gérard





    • Modifié GP79 mardi 19 mai 2015 21:23
    mardi 19 mai 2015 21:10
  • J'ai fais une petite  application.

    Visiblement on ne peut pas sérialiser les collections génériques, mêmes les listes, mais pas de problème pour les tableaux.

    Voici le code complet avec le résultat

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Soap;

    //Il faut ajouter une référence vers l'assembly using System.Runtime.Serialization.Formatters

    namespace TestSerialisable
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void BTNexporter_Click(object sender, EventArgs e)
            {
                try
                {
                    Client l_Client = new Client("GP79");
                    l_Client.ID = 79;
                    l_Client.Telephones = new string[] { "0011223344", "2244668800"};

                    //Opens a file and serializes the object into it in binary format.
                    string l_Nomfichier = "c:\\Affaires\\Testdata.xml";
                   
                    if (System.IO.File.Exists(l_Nomfichier))
                        System.IO.File.Delete(l_Nomfichier);

                    Stream stream = File.Open(l_Nomfichier, FileMode.Create);
                    SoapFormatter formatter = new SoapFormatter();

                    //BinaryFormatter formatter = new BinaryFormatter();

                    formatter.Serialize(stream, l_Client);
                    stream.Close();

                    //Empties obj.
                    l_Client = null;

                    //Opens file "data.xml" and deserializes the object from it.
                    if (System.IO.File.Exists(l_Nomfichier))
                    {
                        stream = File.Open(l_Nomfichier, FileMode.Open);
                        formatter = new SoapFormatter();

                        //formatter = new BinaryFormatter();
                        l_Client = (Client)formatter.Deserialize(stream);
                        stream.Close();
                    }
                   
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message,"GP79", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

            }

            private void BTNfermer_Click(object sender, System.EventArgs e)
            {
                Close();
            }
        }


    //using System;
    //using System.Collections.Generic;
    //using System.Linq;
    //using System.Text;
    //using System.Threading.Tasks;
    //using System.Xml.Serialization;
    //using System.IO;
    //using System.Runtime.Serialization;
    ////using System.Runtime.Serialization.Formatters.Binary;
    //using System.Runtime.Serialization.Formatters.Soap;

        [Serializable]
        public class Client
        {
            string m_Nom;

            [NonSerialized]
            int m_ID;
            string[] m_Telephones;

            public Client(string p_Nom)
            {
                m_Nom = new string(p_Nom.ToCharArray());
                m_ID = -1;
            }

            public string Nom
            {
                get { return m_Nom; }
                set { m_Nom = value; }
            }

            public int ID
            {
                get { return m_ID; }
                set { m_ID = value; }
            }

            public string[] Telephones
            {
                get { return m_Telephones; }
                set { m_Telephones = value; }
            }

        }
    }


    Le fichier xml

    <SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
    <a1:Client id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/TestSerialisable/TestSerialisable%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
    <m_Nom id="ref-3">GP79</m_Nom>
    <m_Telephones href="#ref-4"/>
    </a1:Client>
    <SOAP-ENC:Array id="ref-4" SOAP-ENC:arrayType="xsd:string[2]">
    <item id="ref-5">0011223344</item>
    <item id="ref-6">2244668800</item>
    </SOAP-ENC:Array>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>

    Cordialement
    Gérard


    • Proposé comme réponse GP79 mardi 19 mai 2015 21:49
    • Modifié GP79 mercredi 20 mai 2015 07:08
    • Non proposé comme réponse Duvernet Vincent jeudi 21 mai 2015 13:39
    mardi 19 mai 2015 21:48
  • - pour ton premier post, le problème ne réside pas dans le fait de pouvoir sérializer un objet simple mais un objet avec des classes dérivées.

    Le code de M$ qui fonctionne est :

    public class Orders
    {
        public Book[] Books;
    }   

    Le mien :

    public class Orders
    {
        private Book[] Books;
    
        public Book[] BookArray
        {
            get { return this.Books; }
            set { this.Books = value; }
        }
    }
    Techniquement, c'est la même chose sauf pour le Serializer visiblement.

    - Je n'avais pas pensé à SOAP mais je souhaiterai éviter, ça alourdit trop le XML


    mercredi 20 mai 2015 07:55
  • J'ai résolu une partie du problème grâce à cet article :

    www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly?msg=5062225#xx5062225xx

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    
    namespace Test
    {
        public class Animal : IXmlSerializable
        {
            public Animal() { }
            public Animal(string name) { Name = name; }
            public String Name { get; set; }
            public DateTime Birthday { get; set; }
    
            public System.Xml.Schema.XmlSchema GetSchema() { return null; }
    
            public void ReadXml(System.Xml.XmlReader reader)
            {
                if (reader == null)  throw new ArgumentNullException("reader");
    
                reader.MoveToContent();
                Name = reader.GetAttribute("Name");
                Boolean isEmptyElement = reader.IsEmptyElement; // (1)
                reader.ReadStartElement();
                if (!isEmptyElement) // (1)
                {
                    Birthday = DateTime.ParseExact(reader.ReadElementString("Birthday"), "yyyy-MM-dd", null);
                    reader.ReadEndElement();
                }
            }
    
            public void WriteXml(System.Xml.XmlWriter writer)
            {
                if (writer == null) throw new ArgumentNullException("writer");
    
                writer.WriteAttributeString("Name", Name);
                if (Birthday != DateTime.MinValue)
                {
                    writer.WriteElementString("Birthday", Birthday.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
                }
            }
        }
        public class Dog : Animal
        {
            public Dog() { }
            public Dog(string name) : base (name) { }
        }
        public class Cat : Animal
        {
            public Cat() { }
            public Cat(string name) : base(name) { }
        }
        public class Mouse : Animal
        {
            public Mouse() { }
            public Mouse(string name) : base (name) { }
        }
    
        public class AnimalList
        {
            private Collection<Animal> listOfAnimals;
    
            public AnimalList() { listOfAnimals = new Collection<Animal>(); }
    
            [XmlElement("Dog", typeof(Dog))]
            [XmlElement("Cat", typeof(Cat))]
            [XmlElement("Mouse", typeof(Mouse))]
            public Collection<Animal> List
            {
                get { return listOfAnimals; } 
            }
        }
    
        public class Farm
        {
            public Farm() { Animals = new AnimalList(); }
    
            public AnimalList Animals { get; set; }
        }
    
    
        class Program
        {
            static void Main()
            {
                try
                {
                    Farm farm = new Farm();
                    string filename = @"..\..\farm.xml";
    
    
                    farm.Animals.List.Add(new Cat("Ati"));
                    farm.Animals.List.Add(new Cat("Gribouille"));
                    farm.Animals.List.Add(new Dog("Joker"));
                    farm.Animals.List.Add(new Cat("Joyeuse"));
                    
                    // Writing the file requires a TextWriter instance.
                    using (TextWriter writer = new StreamWriter(filename))
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(Farm));
                        serializer.Serialize(writer, farm);
                    }
    
                    // Read XML file
                    using (TextReader reader = new StreamReader(filename))
                    {
                        XmlSerializer serializer2 = new XmlSerializer(typeof(Farm));
                        Farm farm2 = (Farm)serializer2.Deserialize(reader);
                        Console.WriteLine("Numer of animals : {0}", farm2.Animals.List.Count);
                        foreach (Animal animal in farm2.Animals.List)
                        {
                            Console.WriteLine("Name : {0}", animal.Name);
                        }
                        Console.WriteLine("Press any key to continue...");
                        Console.ReadKey();
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
                }
            }
        }
    }
    

    ce qui produit le XML :

    <?xml version="1.0" encoding="utf-8"?>
    <Farm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <Animals>
        <Cat Name="Ati" />
        <Cat Name="Gribouille" />
        <Dog Name="Joker" />
        <Cat Name="Joyeuse" />
      </Animals>
    </Farm>

    Pour le moment, ça impose de connaître les classes dérivées mais c'est un début.

    Vincent

    jeudi 21 mai 2015 16:12