Problème de désérialisation: erreur lors de la désérialisation à partir d'une version de programme différente

StackOverflow https://stackoverflow.com/questions/6374411

Question

J'ai finalement décidé de publier mon problème après quelques heures passées à chercher des solutions sur Internet et à en essayer.

[Contexte du problème]

Je développe une application qui sera déployée en deux parties:

  • un outil d'importation XML: son rôle est de charger / lire un fichier xml afin de remplir certaines structures de données, qui sont ensuite sérialisées dans un fichier binaire.
  • l'application de l'utilisateur final: elle chargera le fichier binaire généré par l'importateur XML et fera quelques trucs avec les structures de données récupérées.

Pour l'instant, je n'utilise l'Importateur XML que dans les deux cas (c'est-à-dire que je charge d'abord le xml et l'enregistre dans un fichier binaire, puis je rouvre l'Importateur XML et charge mon fichier binaire).

[Problème réel]

Cela fonctionne très bien et je suis capable de récupérer toutes les données que j'avais après le chargement XML, tant que je le fais avec la même version de mon importateur XML . Ce n'est pas viable, car j'aurai besoin d'au moins deux versions différentes, une pour l'importateur XML et une pour l'application utilisateur final. Veuillez noter que les deux versions de l'Importateur XML que j'utilise pour mes tests sont exactement les mêmes concernant le code source et donc les structures de données , la seule différence réside dans le numéro de build (pour forcer une build différente I il suffit d'ajouter un espace quelque part et de reconstruire).

Donc, ce que j'essaie de faire, c'est:

  • Créer une version de mon importateur XML
  • Ouvrez l'Importateur XML, chargez un fichier XML et enregistrez les structures de données résultantes dans un fichier binaire
  • Reconstruire l'importateur XML
  • Ouvrez le nouvel importateur XML, chargez le fichier binaire créé précédemment et récupérez mes structures de données.

Pour le moment, j'obtiens une exception:

SerializationException: Could not find type 'System.Collections.Generic.List`1[[Grid, 74b7fa2fcc11e47f8bc966e9110610a6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadType (System.IO.BinaryReader reader, TypeTag code)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectInstance (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)

Pour votre information (je ne sais pas si utile ou non), le type réel qu'il a du mal à désérialiser est une liste, Grid étant une classe personnalisée (qui est correctement sérialisable, comme je suis capable de le faire en utilisant le même version de XML Importer).

[Solution potentielle]

Je pense que cela vient de quelque part dans l'Assemblée, car j'ai lu de nombreux messages et articles à ce sujet. Cependant, j'ai déjà un classeur personnalisé prenant en charge les différences de noms d'assemblages, ressemblant à ceci:

public sealed class VersionDeserializationBinder : SerializationBinder
{ 
    public override Type BindToType( string assemblyName, string typeName )
    { 
        if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) )
        { 
            Type typeToDeserialize = null; 
            assemblyName = Assembly.GetExecutingAssembly().FullName; 
            // The following line of code returns the type. 
            typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) ); 

            return typeToDeserialize; 
        } 

        return null; 
    }
}

que j'attribue au BinaryFormatter avant de désérialiser ici:

    public static SaveData Load (string filePath) 
    {
        SaveData data = null;//new SaveData ();
        Stream stream;

        stream = File.Open(filePath, FileMode.Open);


        BinaryFormatter bformatter = new BinaryFormatter();
        bformatter.Binder = new VersionDeserializationBinder(); 
        data = (SaveData)bformatter.Deserialize(stream);
        stream.Close();

        Debug.Log("Binary version loaded from " + filePath);

        return data; 
    }

Est-ce que l'un de vous a une idée de la façon dont je pourrais y remédier? Ce serait génial, plutôt s'il vous plaît :)

Était-ce utile?

La solution

Déplacez les bits de travail vers un assemblage séparé et utilisez l'assemblage à la fois dans "serveur" et "client".Sur la base de votre explication du problème, cela devrait contourner le problème de «mauvaise version», si c'est le problème principal.Je prendrais également n'importe quel "modèle" (c'est-à-dire des bits d'état comme Grid) dans un projet de modèle de domaine, et l'utiliserais aux deux endroits.

Autres conseils

Je viens de tomber sur votre fil alors que j'avais le même problème.En particulier, votre exemple de code avec SerializationBinder m'a beaucoup aidé.J'ai juste dû le modifier légèrement pour faire la différence entre mes propres assemblages et ceux de Microsoft.J'espère que cela vous aidera aussi:

sealed class VersionDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;
        string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;

        //my modification
        string currentAssemblyName = currentAssemblyInfo.Split(',')[0];
        if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;

        typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
        return typeToDeserialize;
    }
}

Je pense que le problème est que vous lui dites de rechercher List <> dans l'assembly en cours d'exécution, alors qu'en fait il se trouve dans l'assembly System.Vous ne devez réattribuer le nom de l'assemblage dans votre classeur que si l'assemblage d'origine est l'un des vôtres.

De plus, vous devrez peut-être gérer les types de paramètres pour les génériques spécifiquement dans le classeur, en analysant le nom du type et en vous assurant que les types de paramètres ne sont pas spécifiques à l'assembly étranger lorsque vous retournez le type générique paramétré.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top