web-dev-qa-db-fra.com

Java Sérialisation personnalisée

J'ai un objet qui contient quelques champs non sérialisables que je veux sérialiser. Ils proviennent d'une API distincte que je ne peux pas modifier, donc les rendre sérialisables n'est pas une option. Le principal problème est la classe Location. Il contient quatre choses qui peuvent être sérialisées dont j'aurais besoin, toutes les ints. Comment puis-je utiliser read/writeObject pour créer une méthode de sérialisation personnalisée qui peut faire quelque chose comme ceci:

// writeObject:
List<Integer> loc = new ArrayList<Integer>();
loc.add(location.x);
loc.add(location.y);
loc.add(location.z);
loc.add(location.uid);
// ... serialization code

// readObject:
List<Integer> loc = deserialize(); // Replace with real deserialization
location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
// ... more code

Comment puis-je faire ceci?

61
Alexis King

Java prend en charge Sérialisation personnalisée . Lisez la section Personnaliser le protocole par défaut.

Citer:

Il existe cependant une solution étrange mais astucieuse. En utilisant une fonction intégrée du mécanisme de sérialisation, les développeurs peuvent améliorer le processus normal en fournissant deux méthodes dans leurs fichiers de classe. Ces méthodes sont les suivantes:

  • private void writeObject (ObjectOutputStream out) lève IOException;
  • private void readObject (ObjectInputStream in) lève IOException, ClassNotFoundException;

Dans cette méthode, ce que vous pourriez faire est de le sérialiser dans d'autres formulaires si vous en avez besoin, comme la liste de tableaux pour l'emplacement que vous avez illustrée ou JSON ou un autre format/méthode de données et de le reconstruire sur readObject ()

Avec votre exemple, vous ajoutez le code suivant:



private void writeObject(ObjectOutputStream oos)
throws IOException {
    // default serialization 
    oos.defaultWriteObject();
    // write the object
    List loc = new ArrayList();
    loc.add(location.x);
    loc.add(location.y);
    loc.add(location.z);
    loc.add(location.uid);
    oos.writeObject(loc);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
    // default deserialization
    ois.defaultReadObject();
    List loc = (List)ois.readObject(); // Replace with real deserialization
    location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
    // ... more code

}

63
momo

Semblable à la réponse de @ momo mais sans utiliser une liste et des valeurs int en boîte automatique qui le rendront beaucoup plus compact.

private void writeObject(ObjectOutputStream oos) throws IOException {
    // default serialization 
    oos.defaultWriteObject();
    // write the object
    oos.writeInt(location.x);
    oos.writeInt(location.y);
    oos.writeInt(location.z);
    oos.writeInt(location.uid);
}

private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
    // default deserialization
    ois.defaultReadObject();
    location = new Location(ois.readInt(), ois.readInt(), ois.readInt(), ois.readInt());
    // ... more code

}
33
Peter Lawrey

Si cela doit être Java sérialisation, la seule façon que je connaisse est de redéfinir les readObject() et writeObject() dans toutes les classes ayant une référence à une instance de Location comme indiqué dans la réponse de Momo. Notez que cela ne vous permettra pas de sérialiser un Location[], et vous obligera à sous-classer tous les Collection<Location> apparaissant dans votre code. De plus, il nécessite que les champs de type Location soient marqués comme transitoires, ce qui exclura leurs définitions d'être écrites dans le flux de sérialisation, ce qui peut déjouer la détection de changements de classe incompatibles.

Une meilleure façon serait de simplement remplacer ObjectOutputStream.writeObject. Hélas, cette méthode est final. Vous pouvez remplacer ObjectOutputStream.writeObjectOverride() à la place, mais cette méthode ne peut pas déléguer l'implémentation par défaut, ObjectOutputStream.writeObject0() car cette méthode est private. Bien sûr, vous pouvez invoquer la méthode privée en utilisant la réflexion, mais ...

Par conséquent, je recommande de vérifier vos contraintes. Doit-il être Java sérialisation? Pouvez-vous vraiment pas changer la définition de la classe Location?

Si vous avez le code source de la classe Location, il est assez simple d'ajouter implements Serializable Et de l'ajouter à votre chemin de classe. Oui, vous devrez recommencer chaque fois que vous mettez à niveau la bibliothèque, mais cela pourrait être mieux que l'alternative ...

0
meriton