IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Désérialisation XML avec tableaux


Sujet :

C#

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut Désérialisation XML avec tableaux
    Salut
    -----

    Je rencontre un petit souci avec des classes que je sérialise en XML
    J'ai besoin de sérialiser des propriétés non accessibles en écriture, j'ai donc implémenté l'interface IXmlSerializable dans ma classe, et je stocke tous les infos utiles sous forme d'un simple tableau d'entiers.

    Le reader est rédigé comme ceci (j'ai essayé plusieurs méthode, avec le même résultat) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    public void ReadXml(System.Xml.XmlReader reader)
    {
        while (reader.Read())                                   // parcourir tout le fichier
            if (reader.NodeType == XmlNodeType.Element)         // si trouvé un élément actif
            {
                if (reader.Name == "ArrayOfInt")                // si trouvé la balise de début des infos utiles
                {
                    // créer le désérialiseur
                    XmlSerializer xs = new XmlSerializer(typeof(int[]));
                    // Désérialiser et si vérif ok, affecter les données
                    // si pas OK, exception dans Verify.. catchée dans DomocanSerializer
                    VerifyArrayAndSet( xs.Deserialize(reader) as int[]);
                }
            }
    }
    Et le désérialiseur correspond à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public static T DeserializeXml<T>(string fileName)
    {
        T result = default(T);
        FileStream fileReader = null;
        XmlReader reader = null;
        try
        {
            fileReader = new FileStream(fileName, FileMode.Open);
            XmlSerializer deserializer = new XmlSerializer(typeof(T));
            reader = new XmlTextReader(fileReader);
            result = (T)deserializer.Deserialize(reader);
        }
        catch { }
     
        finally
        {
            if (fileReader != null)
                fileReader.Close();
            if (reader != null)
                reader.Close();
        }
        return result;
    }
    Lorsque je sérialise puis désérialise une instance de ma classe, tout se passe parfaitement bien, je récupère mon instance à la désérialisation.

    Si j'essaye de sérialiser un tableau d'instances de ma classe, tout semble également bien se passer, j'obtiens un fichier de ce type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    <?xml version="1.0" encoding="utf-8"?>
    <ArrayOfTrameCanWithRawData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <TrameCanWithRawData>
        <TrameRawArray>
          <ArrayOfInt>
            <int>80</int>
            <int>1</int>
            <int>0</int>
            <int>0</int>
            <int>-1</int>
            <int>-2</int>
          </ArrayOfInt>
        </TrameRawArray>
      </TrameCanWithRawData>
      <TrameCanWithRawData>
        <TrameRawArray>
          <ArrayOfInt>
            <int>80</int>
            <int>1</int>
            <int>0</int>
            <int>0</int>
            <int>0</int>
            <int>-3</int>
          </ArrayOfInt>
        </TrameRawArray>
      </TrameCanWithRawData>
      <TrameCanWithRawData xsi:nil="true" />
      <TrameCanWithRawData xsi:nil="true" />
      <TrameCanWithRawData xsi:nil="true" />
      <TrameCanWithRawData xsi:nil="true" />
      <TrameCanWithRawData xsi:nil="true" />
    </ArrayOfTrameCanWithRawData>
    Par contre, à la désérialisation, j'obtiens un tableau d'un seul élément ne contenant qu'une seule instance (la dernière).

    Je m'attendais à ce que le désérialiseur crée une nouvelle référence de ma classe à chaque instance trouvée dans le fichier xml, et qu'il déclenche donc un readXml

    Si je piste mon ReadXml, je vois qu'il parcours tout le fichier, et à chaque fois qu'il lit le tableau d'entiers, il affecte ce tableau à la référence en cours, toujours la même.

    Bref, le "Readxml" est appelé une seule fois, pour une unique référence de ma classe, au lieu d'être appelé pour chaque référence du tableau de classes.

    J'ai testé toutes sortes de choses après la ligne

    VerifyArrayAndSet( xs.Deserialize(reader) as int[]);

    de ReadXlm : return , reader.ReadSubtree etc, mais je n'ai rien trouvé qui force la génération d'autant d'instances qu'il y a d'entrées dans le fichier xml.

    Quoi que je fasse, ma désérialisation ne me génère jamais qu'une seule instance de ma classe, fusse-t-elle dans un tableau.

    Qu'ai-je oublié?

    Merci d'avance,
    Claude

    PS: j'ai bien utilisé des méthodes alternatives, comme de détecter dans le Deserialize le type correspondant au tableau de classes, et ensuite de recopier mes classes dans un type sérialisable explicitement prévu, mais ce n'est évidemment pas élégant du tout, même si ça fonctionne.

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    J'ai fini par trouver une solution partielle.
    J'ai remplacé mon ReadXml par ceci (j'ai enlevé la balise <TrameArray>, inutile et dérangeante):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    public void ReadXml(System.Xml.XmlReader reader)
    {
        reader.ReadStartElement();
        reader.MoveToContent();
     
        // créer le désérialiseur
        XmlSerializer xs1 = new XmlSerializer(typeof(int[]));
     
        // si on tombe sur un tableau d'entiers, donc trame pas nulle
        // Affecter le tableau à la trame, passer au champs xml suivant
        if (reader.Name == "ArrayOfInt")
        {
            VerifyArrayAndSet(xs1.Deserialize(reader) as int[]);
            reader.MoveToContent();
            reader.ReadEndElement();
        }
    }
    J'obtiens bien un tableau avec le bon nombre d'éléments.

    Par contre, à la place de tous les éléments null initialement, j'obtiens une entrée correspondant à un élément construit avec le constructeur par défaut.

    Quelqu'un a une solution?

    Merci
    Claude

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    Bon, je me parle à moi-même, mais bon, ça pourrait servir à d'autres.

    J'ai trouvé ceci dans les msdn :

    Avant de passer le contrôle à votre code ReadXml, le désérialiseur examine l'élément XML, détecte les attributs XML spéciaux et effectue des actions sur ceux-ci. Par exemple, si « nil » est true, une valeur Null sera désérialisée et ReadXml n'est pas appelée.
    Donc, mon problème vient de là.
    En ce qui me concerne, mon ReadXml est appelé par le désérialiseur même si nil est à true, par exemple pour ces lignes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      <TrameCanWithRawData xsi:nil="true" />
    Moralité, ce n'est pas mon ReadXml qui a un problème, c'est carrément le désérialiseur qui ne fonctionne pas comme prévu.

    Ma question reste la même: quelqu'un a une idée pour me dépanner?

    Merci d'avance,
    Claude

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    612
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Juin 2008
    Messages : 612
    Points : 1 050
    Points
    1 050
    Par défaut
    Salut
    -----

    Bon, puisque je n'ai pas eu de réponses, je conclus sur ce que j'ai fait:

    Tout d'abord il ne m'a jamais été possible via l'interface IXmlSerializable d'obtenir un tableau de classes dont certaines instances sont nulles: Contrairement à ce qui est indiqué dans les documents officiels, Le désérialiseur XML appelle le constructeur sans paramètre de la classe, puis ReadXml même si la ligne concernant cette instance dans le fichier xml indique "nil = true". On obtient donc une instance non correctement initialisée mais non une entrée "null" dans le tableau récupéré.

    Bref, je sérialise un tableau du genre MaClasse[] qui contient des éléments nulls, par exemple :

    maclasse[0] = instance valide de MaClasse
    maclasse[1] = instance valide de MaClasse
    maclasse[2] = null
    maclasse[3] = null

    J'obtiens un fichier xml correct avec 2 éléments décrivant une instance valide de MaClasse et 2 éléments indiquant <MaClasse xsi:nil="true" />

    Tout est OK

    Si je re-désérialise, j'obtiens un tableau contenant 4 instances de MaClasse, sans aucune valeur nulle, avec les éléments suivants:

    maclasse[0] = instance valide de MaClasse telle qu'avant la sérialisation
    maclasse[1] = instance valide de MaClasse idem
    maclasse[2] = instance créée avec le constructeur par défaut, donc incorrecte, au lieu de null
    maclasse[3] = idem

    Partant de ce constat, j'ai contourné le problème consistant à ne pas vouloir exposer l'accesseur set en utilisant l'outil sgen.exe

    J'ai donc ajouté un accesseur set internat à ma propriété à sérialiser, et un constructeur internal. Et, de cette façon, en enlevant l'interface précédente, on revient à un fonctionnement normal. En désérialisant on retrouve bien nos deux éléments à null.

    Moralité, il semble y avoir un bug au niveau du sérialiseur Xml du framework lorsqu'on tente d'utiliser l'interface IXmlSerializable sur une classe et qu'on désire désérialiser un tableau de cette classe contenant des instances nulles.

    Si quelqu'un a des infos sur ceci, ça continue de m'intéresser.

    A+
    Claude

Discussions similaires

  1. [Tableaux] Tableau vers XML avec une fonction PHP
    Par astrotouf dans le forum Langage
    Réponses: 1
    Dernier message: 01/03/2009, 22h51
  2. [XSD][xerces] validation xml avec un xsd
    Par chand_bing dans le forum Valider
    Réponses: 2
    Dernier message: 05/02/2004, 20h21
  3. [XML][XSL][FOP] transformation xml avec fop
    Par Le Marlou dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 05/12/2003, 16h58
  4. []générer et LIRE du xml avec vb: sélection d'un node
    Par chris21 dans le forum VB 6 et antérieur
    Réponses: 11
    Dernier message: 19/09/2003, 13h14
  5. generer xsl depuis xml avec balises inconues ?
    Par cedre dans le forum XSL/XSLT/XPATH
    Réponses: 7
    Dernier message: 24/06/2003, 09h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo