Mise à jour du 20 mai: J'aurais d'avoir mentionné que les objets en question ont une définition "SerialVersionSionUID" (la même valeur chez OLLD & NOUVEAU), mais la sérialisation échoue avant que ReadObject () soit appelée à l'exception suivante:
Je comprends également un exemple en bas. P> Je travaille avec une application importante qui envoie des objets sérialisés (implémentations sérialisables, pas exernalisable) du client au serveur. Malheureusement, nous avons maintenant une situation dans laquelle un champ existant a changé de type complètement, ce qui brise la sérialisation. P> Le plan consiste à mettre à niveau le côté du serveur en premier. À partir de là, j'ai le contrôle des nouvelles versions des objets sérialisés, ainsi que l'ObjectInputStream pour lire les objets (au premier ancien) provenant des clients. P> J'avais au début de la mise en œuvre de ReadObject ( ) dans la nouvelle version; Cependant, après l'avoir essayé, j'ai découvert que la validation échoue (en raison de types incompatibles) avant la méthode jamais appelée. p> Si je sous-classe ObjecteInpuTstream, puis-je accomplir ce que je veux? P> Encore mieux, existe-t-il des bibliothèques tierces qui font une sorte de sérialisation 'Magic'? Il serait vraiment intéressant de convertir des outils / bibliothèques pouvant convertir un flux d'objets sérialisés en quelque chose comme un éventail de hashmaps ... sans avoir besoin de charger les objets eux-mêmes. Je ne sais pas si il est possible de le faire (convertir un objet sérialisé sur un hashmap sans charger la définition de l'objet elle-même), mais si cela est possible, je pouvais imaginer un outil pouvant convertir un objet sérialisé (ou flux d'objets ) À une nouvelle version, en utilisant, par exemple, un ensemble de propriétés pour les astuces de conversion / règles, etc. p> Merci pour toute suggestion. P> Mise à jour du 20 mai - Exemple de source ci-dessous - le champ "numéro" dans TestData passe à partir d'un "INT" dans l'ancienne version à une "longue" dans la nouvelle version. Remarque ReadObject () Dans la nouvelle version de TestData n'est pas appelée, car une exception est levée avant de ne jamais y arriver: p> Exception dans le fil "Main" Java.IO.InvalidClsSException: TestDATA; Types incompatibles pour le numéro de champ p> ci-dessous est la source. Enregistrez-le dans un dossier, puis créez les sous-dossiers 'Old »et« Nouveau ». Mettez la version «ancienne» de la classe TestData dans le dossier «Old» et la nouvelle version dans «Nouveau». Placez les classes «ERITITE» et «Readit» dans le dossier principal (parent de l'ancien / nouveau). 'CD' dans le "vieux" dossier et compiler avec: puis exécuté avec: p> [Version ancienne] Testdata.java B> P> exception dans le fil "MAIN" Java.IO.InvalidClsSException: TestData; Types incompatibles pour le numéro de champ code> p>
javac -g -classpath. TestData.java code>
... puis faites la même chose dans le «nouveau» dossier. "CD" retour au dossier parent et compiler Evident / Readit: p>
import java.io.*;
public class ReadIt
{
public static void main(String args[])
throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("TestData_old.ser"));
TestData d = (TestData)ois.readObject();
ois.close();
System.out.println("Number = " + d.number);
}
}
4 Réponses :
Votre problème immédiat semble être que vous n'avez pas défini modifié les chaînes SerialVersionUID pour vos cours. Si vous ne faites pas cela, le code de sérialisation et de désérialisation de l'objet génère les UID basés sur la structure de représentation des types envoyés et reçus. Si vous modifiez le type à une extrémité et non l'autre, les UID ne correspondent pas. Si vous corrigez les UIDS, le code de lecteur aura dépassé la vérification UID et que votre méthode En dehors de cela, ma suggestion serait: p>
Essayez de changer tous les clients et serveurs en même temps em> de sorte que vous n'avez pas besoin d'utiliser ReadObject / WriteObject Hacks P> LI>
Si ce n'est pas pratique, essayez de Si ce n'est pas pratique, version de votre protocole client / serveur. p> li>
ul>
Je doute que vous obtiendrez une traction en sous-classant les classes Objectstream. (Jetez un coup d'œil au code ... dans le code OpenJDK.) P> lisaBject code> aura une chance de "contourner" les différences de données sérialisées. P>
Notez que j'ai négligé de mentionner que je dispose de «SerialVersionUid» défini (à la même valeur) dans la version ancienne et nouvelle de la classe. J'ai inclus un exemple de code maintenant. Merci pour les commentaires. Je vais voir s'il y a un moyen d'accomplir le premier (Mettre à jour le client / serveur en même temps) - le problème concerne les opérations de 24 heures, etc.
Après l'avoir essayé, j'ai découvert que la validation échoue (en raison de types incompatibles) avant la méthode jamais appelée p> blockQuote>
C'est parce que vous n'avez pas défini un
SerialVersionSionUID code>. C'est facile à réparer. Il suffit d'exécuter l'outil code> Serialver code> sur la version ancienne em> des classes, puis collez la sortie dans le nouveau code source. Vous May em> puis être capable d'écrire votre méthode
lisechatObject () code> de manière compatible. P>
En outre, vous pouvez regarder dans l'écriture
readresolve () code> et
writerePlace () code> méthodes ou définissant
SERIALPERSISTENTFields code> de manière compatible, si c'est possible. Voir le spécification de sérialisation de l'objet. P >
Voir aussi les précieuses suggestions de @Stephenc. p>
Postez votre modification forte> Je vous suggère de modifier le nom de la variable avec son type. Cela compte comme une suppression plus une insertion, qui est compatible avec la sérialisation et travaillerez aussi longtemps que vous ne voulez pas que l'ancien numéro INT soit lu dans le nouveau nombre long (pourquoi longtemps au lieu de long?). Si vous avez besoin de cela, je vous suggère de laisser un numéro INT et d'ajouter un nouveau champ long / long avec un nom différent et de modifier ReadObject () pour définir le nouveau champ sur 'Number' s'il n'est pas déjà défini par DEFAULTREADOBJECT () . p>
Notez que j'ai négligé de mentionner que je dispose de «SerialVersionUid» défini (à la même valeur) dans la version ancienne et nouvelle de la classe. J'ai inclus un exemple de code maintenant. Merci pour les commentaires.
Si vous modifiez le type de champ, vous devez modifier son nom. Sinon, vous obtiendrez l'erreur que vous voyez. Si vous souhaitez définir le nouveau nom de champ de l'ancienne Données (ou le calculez d'une autre manière pendant l'objet Lire), vous pouvez faire quelque chose comme ceci:
public class TestData implements java.io.Serializable { private static final long serialVersionUID = 2L; public Long numberL; //Changed from "int number;" private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println("[TestData NEW] readObject() called..."); ois.defaultReadObject(); if (numberL == null) { // deal with old structure GetField fields = ois.readFields(); numberL = fields.get("number", 0L); // autoboxes old value } } }
Vous pouvez continuer à utiliser la sérialisation même lorsque des modifications des classes rendront les anciennes données sérialisées incompatibles avec la nouvelle classe, si vous implémentez externalisable et écrivez un champ supplémentaire indiquant la version avant de rédiger les données de la classe. Cela permet à Readexternal de gérer les anciennes versions de la manière dont vous spécifiez. Je ne connais aucun moyen de le faire automatiquement, mais utiliser cette méthode manuelle peut fonctionner pour vous.
Voici les classes modifiées qui compileront et fonctionnent sans jeter une exception. P>
Old / TestData. java p> nouveau / testdata.java p> Ces classes peuvent être exécutées avec les affirmations suivantes p> xxx pré> changer une classe à mettre en œuvre exernalisable au lieu de sérialisable entraînera l'exception suivante. P> Exception in thread "main" java.io.InvalidClassException: TestData; Serializable incompatible with Externalizable
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:634)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at ReadIt.main(ReadIt.java:10)
Le jeu de hacks standard de facto ici est de définir explicitement un SerialVersionSionUID pour toute classe mise en œuvre sérialisable, puis remplace la méthode ReadObject. Lorsque des changements se produisent, le code plus récent peut mettre la logique en place pour traiter avec l'ancienne forme
Lisez dans l'ancien objet en utilisant l'ancienne méthode, extrayez-la des données dans le nouvel objet et enregistrez-la.
J'ai oublié de mentionner que j'ai un ensemble "SerialversionSionUid", mais une exception se produit avant que ReadObject () soit appelée dans la nouvelle version. Merci.
Notez également que les données «réelles» en question sont fondamentalement un vecteur d'objets de données plats, où les champs sont des types primitifs ou intégrés (entier, chaîne, etc.) Ce que j'aimerais vraiment avoir, c'est essentiellement la capacité Pour effectuer un traitement de type réfléchissant sur un flux sérialisé, sans avoir à charger une référence (ancienne ou nouvelle) à l'objet en question. Mais peut-être que le format sérialisé ne fournit pas suffisamment d'informations à ce sujet pour faire ce genre de chose.