Come posso deserializzare l’object, se è stato spostato su un altro pacchetto o rinominato?

Si consideri la seguente situazione:

Esiste un file di serializzazione, creato dalla versione precedente dell’applicazione. Sfortunatamente, il pacchetto è cambiato per la class, che è stata serializzata. E ora ho bisogno di caricare le informazioni da questo file nella stessa class, ma in un pacchetto diverso. Questa class ha definito serialVersionUID e non è stato modificato (ovvero è compatibile).

Domanda: è ansible caricare le nuove istanze di class da questo file utilizzando qualsiasi trucchetto (ad eccezione della semplice copia della class nel vecchio pacchetto e quindi utilizzando la logica del wrapper di deserializzazione)? È ansible utilizzare readResolve() per recuperare dallo spostamento / ridenominazione della class? In caso contrario, per favore, spiega perché.

Domanda: è ansible caricare le nuove istanze di class da questo file utilizzando qualsiasi trucchetto (ad eccezione della semplice copia della class nel vecchio pacchetto e quindi utilizzando la logica del wrapper di deserializzazione)?

Non penso ci siano altri “trucchi” che potresti usare che non comportino almeno una reimplementazione parziale del protocollo di serializzazione.

È ansible utilizzare readResolve () per recuperare dallo spostamento / ridenominazione della class? In caso contrario, per favore, spiega perché.

No, perché il meccanismo di deserializzazione fallirà molto prima, nella fase in cui tenta di localizzare la class che viene deserializzata, non ha modo di sapere che una class in un altro pacchetto ha un metodo readResolve() che dovrebbe usare.

È ansible:

 class HackedObjectInputStream extends ObjectInputStream { public HackedObjectInputStream(InputStream in) throws IOException { super(in); } @Override protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); if (resultClassDescriptor.getName().equals("oldpackage.Clazz")) resultClassDescriptor = ObjectStreamClass.lookup(newpackage.Clazz.class); return resultClassDescriptor; } } 

Ciò consente inoltre di ignorare la mancata corrispondenza di serialVersionUID o addirittura deserializzare una class se la sua struttura di campo è stata modificata.

Probabilmente la soluzione migliore è ricreare la vecchia class (nome, pacchetto e ID seriale), leggere nel modulo serializzato, quindi copiare i dati in un’istanza del nuovo object e resettializzarla.

Se disponi di molti di questi oggetti serializzati, potresti scrivere un piccolo script per fare in modo che il “cambiamento dello schema” venga eseguito in un colpo solo.

Un’altra opzione è resuscitare la vecchia class e implementare il suo metodo readResolve per restituire un’istanza della nuova class (forse dichiarando un costruttore di copie). Personalmente penso di andare per lo script di modifica dello schema e quindi cancellare la vecchia class per sempre.

Se usi Cygnus Hex Editor puoi modificare manualmente il nome del pacchetto / class.

Se il nuovo nome (sempre incluso il pacchetto) ha le stesse dimensioni, puoi semplicemente sostituire il vecchio nome con il nuovo nome, ma se la dimensione è cambiata devi aggiornare i primi 2 caratteri prima del nome con una nuova nuova lunghezza.

Fare clic con il tasto destro su Tipi di dati standard e passare a Big Endian.

La lunghezza è una parola firmata.

Per esempio:

 00 0E 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65 . . package . S ample 

è come package.Sample è scritto. 00 0E significa 14, il numero di caratteri che “pacchetto.Sample” ha.

Se vogliamo passare a newpackage.Sample, sostituiamo quella stringa a:

 00 12 6E 65 77 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65 . . newpackage . S ample 

00 12 significa 18, il numero di caratteri “newpackage.Sample” ha.

E ovviamente puoi fare un patcher per aggiornarlo automaticamente.

Non credo sia ansible fare ciò che vuoi.

Il formato del file di serializzazione mantiene i nomi delle classi. In dettaglio ha struttura successiva:

AC ED

numero di versione del protocollo

dati dell’object

descrizione della class dell’object

La descrizione della class ha il seguente formato:

nome completo della class

ID univoco della versione seriale (SHA1 dai campi e firme dei metodi)

opzioni di serializzazione

descrittori di campo

Quando si tenta di deserializzare il meccanismo di serializzazione degli oggetti confronta prima i nomi delle classi (e non si passa questo passaggio), quindi confronta serialVersionUID e solo dopo aver passato questi due passaggi deserializza l’object.

Aggiunta alla modalità di modifica esadecimale.

Ha funzionato per me ed è stato più facile sostituire il vecchio nome del pacchetto con quelli nuovi invece di implementare sostituzioni di class che sostituiscono ObjectInputStream. Soprattutto perché c’erano anche classi anonime.

Ecco uno script che sostituisce il vecchio percorso di class con il nuovo percorso di class in un formato binario.

Ecco un contenuto del mio script hexreplace.sh :

 #!/bin/bash set -xue OLD_STR=$(echo -n $1 | hexdump -ve '1/1 "%.2X"') NEW_STR=$(echo -n $2 | hexdump -ve '1/1 "%.2X"') SRC_FILE=$3 DST_FILE=$4 TMP_FILE=$(mktemp /tmp/bin.patched.XXXXXXXXXX) [ -f $SRC_FILE ] hexdump -ve '1/1 "%.2X"' "$SRC_FILE" | sed "s/$OLD_STR/$NEW_STR/g" | xxd -r -p > "$TMP_FILE" mv "$TMP_FILE" "$DST_FILE" 

Correre

 hexreplace.sh old.class.path new.class.path source_file destination_file 

Lo script funziona correttamente quando i file di origine e destinazione sono gli stessi.