1

Salut,
Je dois sérialiser une classe en XML depuis un site ASP.NET C#

J'ai vu au moins deux solutions :
1. Utiliser la classe XmlSerializer => http://www.devhood.com/Tutorials/tutorial_details.aspx?tutorial_id=236
2 . Utiliser l'annotation [Serializable] et, en cas de besoin, dériver sa classe de l'interface ISerializable, le tout étant sérialisé via un SoapFormatter.

En fait, dans l'application sur laquelle je travaille c'est la solution 1 qui est utilisée. Ça fonctionnait, sauf que j'ai ajouté un attribut qui n'est pas un type de base (= une classe à moi, en fait) et il se trouve que la valeur de l'attribut en question n'est pas sauvegardée dans le XML.... mad rage vtff
Ça fait un moment que je m'épuise sur cette connerie, alors que la deuxième technique de sérialisation a l'air de fonctionner à merveille (dans un test à part)

Concrètement, quelle est l'intérêt d'une technique par rapport à l'autre ? Qu'est ce qui peut déconner dans la première version à votre avis ? Faut-il implémenter quelque chose de spécial ???( j'ai ajouté un attribut de type string et il se trouve que dans ce cas, la valeur est effectivement stockée.. !)

Merci d'avance hehe



PS : en plus la première technique ne stocke que les attributs publics gol

2

hu ? Pkoi il n'est pas dans yn24 mon topic ? confus
EDIT : ça y est, il est sorti du chapeau trilove (merci yAro ^^)

3

Dans tous les cas une classe que tu veux sérializer devra être marquée [Serializable], ensuite les elements XmlAttribute & co permettent de configurer la manière dont la sérialization sera effectuée, c'est à dire changer le nom des balises, les attributs, le fait que des propriétés soient ignorées ou non, etc.

Si ton attribut n'est pas sérializé, ajoute l'attribut et regarde aussi s'il n'y a pas d'exceptions qui sont lancées.

sinon voilà une classe générique qui permet de tout sérializer/désérializer, c'est toujours utilie dans un projet wink

public static class GenericSerializer<T>
    {
        public static string Serialize(object obj)
        {
            XmlSerializer ser = new XmlSerializer(obj.GetType());
            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                ser.Serialize(sw, obj);
            }

            return sb.ToString();
        }

        public static T Deserialize(string str)
        {
            if (string.IsNullOrEmpty(str)) return default(T);

            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (TextReader tr = new StringReader(str))
            {
                T b = (T)serializer.Deserialize(tr);
                tr.Close();

                return b;
            }
        }
    }
avatar
Webmaster et développeur du site. Pour tout probleme ou question envoyez un mini message ou mail.

Suivez l'actualité de tous vos site préférés sur yAronews : http://ns.yaronet.com =)

4

Ajout : le fait que la sérialization ne stocke que les attributs publiques c'est aussi normal, pour stocker les attributs privés, il faut utiliser la sérialization binaire.
avatar
Webmaster et développeur du site. Pour tout probleme ou question envoyez un mini message ou mail.

Suivez l'actualité de tous vos site préférés sur yAronews : http://ns.yaronet.com =)

5

Merci : je vais regarder ça smile

6

Bon, j'ai réussi à réaliser un petit testcase :

Voici la page :
<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
        <asp:Button ID="Button1" runat="server" onclick="Button1_Click" 
            Text="Serialisation avec attribut" />
    
    </div>
    <asp:Button ID="Button2" runat="server" onclick="Button2_Click" 
        Text="Serialisation avec XmlSerializer " />
    
    <asp:Label ID="outLabel" runat="server" Text="Label"></asp:Label>
    </form>
</body>
</html>


et son rendu dans le designer :
ctJo.png

Voici le code associé aux boutons :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Text;


public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }


    protected void Button1_Click( object sender, EventArgs e )
    {
       TopLevelClass c = new TopLevelClass("001");
       MemoryStream memStream = new MemoryStream();
       try {
          SoapFormatter soapFormatter = new SoapFormatter();
          soapFormatter.Serialize(memStream, c);

          byte[] buff = memStream.GetBuffer();

          string soapOutput = "";

          foreach ( byte b in buff )
             soapOutput += (char)b;


          outLabel.Text = soapOutput.Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", "<br>");
       }
       finally {
          memStream.Close();
       }
    }




    protected void Button2_Click( object sender, EventArgs e )
    {
       TopLevelClass c = new TopLevelClass("001");
       //TextWriter writer = new StringWriter();
       XmlSerializer serializer = new XmlSerializer(c.GetType());
       //serializer.Serialize(writer, c);
       //writer.Close();


       StringBuilder sb = new StringBuilder();
       using ( StringWriter sw = new StringWriter(sb) ) {
          serializer.Serialize(sw, c);
       }

       outLabel.Text = sb.ToString().Replace("<", "&lt;").Replace(">", "&gt;").Replace("\n", "<br>"); 

    }
}




La classe que je veux sérialiser :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;


[Serializable]
public class TopLevelClass
      //: ISerializable
{
   private TopLevelClass() { }
      public TopLevelClass( string attValue )
      {
         stringAttr = attValue;
         innerClassAttr = new InnerClass(145);
      }

   [Serializable]
   public class InnerClass
   {
      private InnerClass() { }
      public InnerClass( int i )
      {
         this.i = i;
      }
      int i ;
   }
   public string stringAttr ;
   public InnerClass innerClassAttr;


   //#region ISerializable Membres

   //void ISerializable.GetObjectData( SerializationInfo info, 
   //                                  StreamingContext context )
   //{
   //   info.AddValue("huhu", "hoho");
   //}

   //#endregion
}









ERF : bon, finalement ça ne vaut pas le coût de pousser plus loin, j'ai juste oublié de mettre mon attribut d'innerClass en public... M'enfin ça semble complètement idiot cette limitation... Y a t il une justification ?
Donc en gros ça veut dire que je suis obligé de casser l'encapsulation de ma classe pour la sérialiser en XML.. ?? confus neutral tritop
À noter que la méthode du bouton_1 sérialise tout, y compris les attributs non publics tels que l'attribut "i" de InnerClass ! Mais j'ai un peu la flemme de modifier le code existant dans le projet sur lequel je travaille... (effets de bords probables et je maîtrise mal le C# :/)
Du coup je pense que je vais passer mon attribut en public.... On n'a vraiment pas le choix avec un XMLSerializer ? sad

7

PS : en plus la méthode du XMLSerializer suxe doublement car elle impose le constructeur par défaut !

Finalement, je suppose qu'elle consiste à instancier la classe avec le constructeur par défaut avant d'affecter les attributs successivement... Ils auraient pu se débrouiller pour que ça moins intrusif, quand même neutral


Bref, vu mon niveau en C# je raconte peut être n'importe quoi, mais dans ce cas, je serais heureux qu'on me détrompe hehe

8

(Toi t'as fais du java grin)

Alors ... l'XMLSerializer ne serialize pas les membres privés pour préserver le contrôle sur les accès à la variable, je m'explique. En .net généralement on utilise des propriétés pour chaque variable qu'on veut rendre accessible :

        private int maVar;

        public int MaVar
        {
            get { return maVar; }
            set { maVar = value; }
        }


Ceci permet de manipuler this.maVar en interne de la manière que l'on souhaite, et protéger ou non l'accès externe.
Tu peux donc garder tes membres privés et créer des propriétés pour y accéder. Ce sont ces propriétés qui vont ensuite être sérializées.

Sinon SoapFormatter est effectivement un objet permettant d'aussi sérialiser, mais différement. Il répond aux normes SOAP 1.1 et va sortir de l'XML certes, mais au format SOAP et non personalisable. Il permet par contre de sérializer du privé, des références circulaires, etc. Il est par contre moins performant que l'XMLSerializer ^^

avatar
Webmaster et développeur du site. Pour tout probleme ou question envoyez un mini message ou mail.

Suivez l'actualité de tous vos site préférés sur yAronews : http://ns.yaronet.com =)

9

(cross avec le ./7 ^^)
Pen^2 (./7) :
PS : en plus la méthode du XMLSerializer suxe doublement car elle impose le constructeur par défaut !

Finalement, je suppose qu'elle consiste à instancier la classe avec le constructeur par défaut avant d'affecter les attributs successivement... Ils auraient pu se débrouiller pour que ça moins intrusif, quand même neutral


Bref, vu mon niveau en C# je raconte peut être n'importe quoi, mais dans ce cas, je serais heureux qu'on me détrompe hehe


Nan elle fait pas ca ... en fait lorsque tu appelles le constructeur d'XmlSerializer, il va construire au runtime un objet interne qui va être destiné a serializer/désérializer ton objet métier. Il ne peut de ce fait avoir accès qu'aux membres publics .... mais du coup ca augmente les perfs (pour avoir accès aux membres privés y'a pas 36 solutions, faut faire de la reflection, et ca c'est par defaut lent).
avatar
Webmaster et développeur du site. Pour tout probleme ou question envoyez un mini message ou mail.

Suivez l'actualité de tous vos site préférés sur yAronews : http://ns.yaronet.com =)

10

(Ah, ça se voit ? grin (à quoi ?))

Sinon oui je connais les propriétés (leur existence en tous cas grin), mais ça revient au même que de laisser la variable publique non ? (à part que je trouve ça plus pratique pour débugguer grin)
Là en gros je ne voulais pas qu'on ait accès ni en lecture, et encore moins en écriture à mon attribut privé depuis l'extérieur sad


PS : bien sûr avec les propriétés on garde l'encapsulation (on ne sait pas réellement ce à quoi on accède depuis l'extérieur de la classe), mais on peut toujours modifier ce quelque chose depuis l'extérieur si on autorise le set sad Et moi je n'en avais pas envie grin)

11

yAro (./9) :
Nan elle fait pas ca ... en fait lorsque tu appelles le constructeur d'XmlSerializer, il va construire au runtime un objet interne qui va être destiné a serializer/désérializer ton objet métier. Il ne peut de ce fait avoir accès qu'aux membres publics .... mais du coup ca augmente les perfs (pour avoir accès aux membres privés y'a pas 36 solutions, faut faire de la reflection, et ca c'est par defaut lent).

OK merci. M'enfin pour moi une sérialisation suivie d'une désérialisation ne devrait pas perdre la moitié de la classe en route. C'est peut être plus rapide, mais du coup j'ai du mal à voir l'utilité confus

12

Alors utilise la sérialization binaire smile
avatar
Webmaster et développeur du site. Pour tout probleme ou question envoyez un mini message ou mail.

Suivez l'actualité de tous vos site préférés sur yAronews : http://ns.yaronet.com =)

13

Hélas, mille fois hélas, ce n'est pas moi qui décide tsss
Bon, je vais rentrer, j'ai faim. Merci beaucoup pour toutes ces explications en tous cas smile
Bonne soirée !

PS : Finalement, je vois cette sérialisation XML plus comme un export des données qu'une véritable sérialisation... J'ai bon ?

14

Le XMLSerializer, étant basé sur la réflexion, c'est surtout un moyen rapide de sérialiser et désérialiser des POD. La sérialisation XML ne possédant pas de constructeur personnalisé, les membres en lecture seule ou privés ne peuvent pas être sérialisés simplement.
Par contre, on peut "personnaliser" la sérialisation XML en implémentant l'interface IXMLSerialisable, mais ça la désérialisation se fait toujours par {constructeur par défaut + désérialisation après construction} (comme la sérialisation COM, d'ailleurs).

À l'inverse, la sérialisation "normale" permet plus de trucs, utilise un vrai constructeur, et possède le droit ReflectionPermission(MemberAccess) pour invoquer le constructeur en question même s'il est privé. Le revers de la médaille, c'est qu'il faut écrire plus de code (et en plus, il me semble que c'est orthogonal à la sérialisation XML et ne peut pas s'y substituer, mais je n'ai pas vérifié).

En clair, ma "rule of thumb", c'est de limiter la sérialisation XML aux POD. Mais je peux faire plus compliqué si nécessaire.
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.