Serializzazione personalizzata Java

Ho un object che contiene alcuni campi non serializzabili che voglio serializzare. Provengono da un’API separata che non posso modificare, quindi renderli serializzabili non è un’opzione. Il problema principale è la class Location. Contiene quattro cose che possono essere serializzate di cui avrei bisogno, tutti i tipi. Come posso usare read / writeObject per creare un metodo di serializzazione personalizzato che può fare qualcosa di simile a questo:

// writeObject: List loc = new ArrayList(); loc.add(location.x); loc.add(location.y); loc.add(location.z); loc.add(location.uid); // ... serialization code // readObject: List loc = deserialize(); // Replace with real deserialization location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3)); // ... more code 

Come posso fare questo?

Java supporta la serializzazione personalizzata . Leggi la sezione Personalizza il protocollo predefinito.

Per citare:

C’è, tuttavia, una soluzione strana ma furba. Utilizzando una funzionalità integrata del meccanismo di serializzazione, gli sviluppatori possono migliorare il normale processo fornendo due metodi all’interno dei propri file di class. Questi metodi sono:

  • private void writeObject (ObjectOutputStream out) genera IOException;
  • private void readObject (ObjectInputStream in) genera IOException, ClassNotFoundException;

In questo metodo, ciò che si potrebbe fare è serializzarlo in altri moduli, se necessario, come ArrayList per Location che hai illustrato o JSON o altro formato / metodo dati e ricostruirlo di nuovo su readObject ()

Con il tuo esempio, aggiungi il seguente codice:

 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 } 

Simile alla risposta di @ momo, ma senza utilizzare un elenco e valori int auto-inscatolati che lo renderanno molto più compatto.

 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 } 

Se deve essere serializzazione Java, l’unico modo che conosco è ridefinire readObject() e writeObject() in tutte le classi avendo un riferimento a un’istanza di Location come mostrato nella risposta di Momo. Nota che questo non ti permetterà di serializzare una Location[] , e ti richiederà di sottoclass tutta la Collection appare nel tuo codice. Inoltre, richiede che i campi di tipo Location siano contrassegnati come temporanei, il che escluderà le loro definizioni dall’essere scritti nel stream di serializzazione, rendendo ansible il rilevamento di modifiche di class incompatibili.

Un modo migliore sarebbe semplicemente sostituire ObjectOutputStream.writeObject . Ahimè, quel metodo è final . È ansible sovrascrivere ObjectOutputStream.writeObjectOverride() , invece, ma tale metodo non può debind l’implementazione predefinita, ObjectOutputStream.writeObject0() perché tale metodo è private . Ovviamente, puoi invocare il metodo privato usando il reflection, ma …

Pertanto, ti consiglio di verificare i tuoi vincoli. Deve essere la serializzazione Java? Puoi davvero non cambiare la definizione di Location della class?

Se hai il codice sorgente in Location , è abbastanza semplice aggiungere implements Serializable e aggiungerlo al classpath. Sì, dovrai farlo di nuovo ogni volta che aggiorni la libreria, ma potrebbe essere meglio dell’alternativa …