Come serializzare un oggetto in una stringa


150

Sono in grado di serializzare un oggetto in un file e quindi ripristinarlo di nuovo, come mostrato nello snippet di codice successivo. Vorrei serializzare l'oggetto in una stringa e archiviarlo in un database. Qualcuno può aiutarmi?

LinkedList<Diff_match_patch.Patch> patches = // whatever...
FileOutputStream fileStream = new FileOutputStream("foo.ser");
ObjectOutputStream os = new ObjectOutputStream(fileStream);
os.writeObject(patches1);
os.close();

FileInputStream fileInputStream = new FileInputStream("foo.ser");
ObjectInputStream oInputStream = new ObjectInputStream(fileInputStream);
Object one = oInputStream.readObject();
LinkedList<Diff_match_patch.Patch> patches3 = (LinkedList<Diff_match_patch.Patch>) one;
os.close();

Risposte:


270

Sergio:

È necessario utilizzare BLOB . È piuttosto semplice con JDBC.

Il problema con il secondo codice che hai pubblicato è la codifica. È inoltre necessario codificare i byte per assicurarsi che nessuno di essi abbia esito negativo.

Se vuoi ancora scriverlo in una stringa puoi codificare i byte usando java.util.Base64 .

Tuttavia dovresti usare CLOB come tipo di dati perché non sai per quanto tempo saranno i dati serializzati.

Ecco un esempio di come usarlo.

import java.util.*;
import java.io.*;

/** 
 * Usage sample serializing SomeClass instance 
 */
public class ToStringSample {

    public static void main( String [] args )  throws IOException,
                                                      ClassNotFoundException {
        String string = toString( new SomeClass() );
        System.out.println(" Encoded serialized version " );
        System.out.println( string );
        SomeClass some = ( SomeClass ) fromString( string );
        System.out.println( "\n\nReconstituted object");
        System.out.println( some );


    }

    /** Read the object from Base64 string. */
   private static Object fromString( String s ) throws IOException ,
                                                       ClassNotFoundException {
        byte [] data = Base64.getDecoder().decode( s );
        ObjectInputStream ois = new ObjectInputStream( 
                                        new ByteArrayInputStream(  data ) );
        Object o  = ois.readObject();
        ois.close();
        return o;
   }

    /** Write the object to a Base64 string. */
    private static String toString( Serializable o ) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream( baos );
        oos.writeObject( o );
        oos.close();
        return Base64.getEncoder().encodeToString(baos.toByteArray()); 
    }
}

/** Test subject. A very simple class. */ 
class SomeClass implements Serializable {

    private final static long serialVersionUID = 1; // See Nick's comment below

    int i    = Integer.MAX_VALUE;
    String s = "ABCDEFGHIJKLMNOP";
    Double d = new Double( -1.0 );
    public String toString(){
        return  "SomeClass instance says: Don't worry, " 
              + "I'm healthy. Look, my data is i = " + i  
              + ", s = " + s + ", d = " + d;
    }
}

Produzione:

C:\samples>javac *.java

C:\samples>java ToStringSample
Encoded serialized version
rO0ABXNyAAlTb21lQ2xhc3MAAAAAAAAAAQIAA0kAAWlMAAFkdAASTGphdmEvbGFuZy9Eb3VibGU7T
AABc3QAEkxqYXZhL2xhbmcvU3RyaW5nO3hwf////3NyABBqYXZhLmxhbmcuRG91YmxlgLPCSilr+w
QCAAFEAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cL/wAAAAAAAAdAAQQUJ
DREVGR0hJSktMTU5PUA==


Reconstituted object
SomeClass instance says: Don't worry, I'm healthy. Look, my data is i = 2147483647, s = ABCDEFGHIJKLMNOP, d = -1.0

NOTA : per Java 7 e precedenti è possibile vedere la risposta originale qui


+1 se hai davvero bisogno di stringhe, allora base64 + clob è la strada da percorrere.
John Gardner,

6
+1, piccolo miglioramento. Meglio usare l'interfaccia Serializable anziché il semplice Object nel metodo toString (): String toString (oggetto serializzabile) statico privato
tefozi,

4
Se proviamo a memorizzare l'oggetto come array di byte anziché come stringa, possiamo ottenere il samething senza usare BASE64.
Sudar,

2
Il difetto fatale in questo è che le definizioni di classe tendono a cambiare nel tempo - se si verifica un tale cambiamento, non sarai in grado di deserializzare! L'aggiunta di un serialVersionUIDto SomeClassproteggerà dall'aggiunta di nuovi campi, ma se i campi vengono rimossi verrai avvitato. Vale la pena leggere ciò che Joshua Bloch ha da dire su questo in Effective Java - books.google.co.uk/…
Nick Holt,

1
Da Java 8 ora esiste java.util.Base64. È necessario aggiornare la risposta: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder () decode (s).;
drUniversalis,

12

Che ne dici di scrivere i dati su un ByteArrayOutputStream invece di un FileOutputStream?

Altrimenti, è possibile serializzare l'oggetto utilizzando XMLEncoder, persistere l'XML, quindi deserializzare tramite XMLDecoder.


8

Grazie per le risposte grandi e veloci. Darò immediatamente alcuni voti per riconoscere il tuo aiuto. Ho codificato la migliore soluzione secondo me in base alle tue risposte.

LinkedList<Patch> patches1 = diff.patch_make(text2, text1);
try {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(bos);
    os.writeObject(patches1);
    String serialized_patches1 = bos.toString();
    os.close();


    ByteArrayInputStream bis = new ByteArrayInputStream(serialized_patches1.getBytes());
    ObjectInputStream oInputStream = new ObjectInputStream(bis);
    LinkedList<Patch> restored_patches1 = (LinkedList<Patch>) oInputStream.readObject();            



        // patches1 equals restored_patches1
    oInputStream.close();
} catch(Exception ex) {
    ex.printStackTrace();
}

Nota che non ho considerato l'utilizzo di JSON perché è meno efficiente.

Nota: prenderò in considerazione il tuo consiglio di non memorizzare oggetti serializzati come stringhe nel database ma byte [].


3
"ByteArrayOutputStream.toString converte utilizzando la codifica predefinita della piattaforma . Sei sicuro di volerlo? Soprattutto perché un array di byte arbitrari non è valido UTF8. Inoltre, il database lo distruggerà."
Tom Hawtin - tackline il

Dovresti prendere sul serio il commento sopra di Tom Hawtin
anjanb

Per non parlare della conservazione a lungo termine di oggetti serializzati non è una grande idea e non è raccomandata
Steve g

"Nota che non ho considerato l'utilizzo di JSON perché è meno efficiente." Che ne dici di usare i buffer di protocollo di google per efficienza? Inoltre, l'idea di Steve G. ha perfettamente senso. Un modo per archiviare i dati serializzati in un DB ma renderli disponibili per lingue diverse da Java è i buffer di protocollo.
Anjanb,

5

Approccio Java8, convertendo Object da / in String, ispirato alla risposta di OscarRyz . Per la de- / codifica, è richiesto e utilizzato java.util.Base64 .

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;
import java.util.Optional;

final class ObjectHelper {

  private ObjectHelper() {}

  static Optional<String> convertToString(final Serializable object) {
    try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos)) {
      oos.writeObject(object);
      return Optional.of(Base64.getEncoder().encodeToString(baos.toByteArray()));
    } catch (final IOException e) {
      e.printStackTrace();
      return Optional.empty();
    }
  }

  static <T extends Serializable> Optional<T> convertFrom(final String objectAsString) {
    final byte[] data = Base64.getDecoder().decode(objectAsString);
    try (final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
      return Optional.of((T) ois.readObject());
    } catch (final IOException | ClassNotFoundException e) {
      e.printStackTrace();
      return Optional.empty();
    }
  }
}

Perché questa è un'interfaccia piuttosto che una classe?
simonalexander2005,

@ simonalexander2005 Nota significativa, non vorrei più utilizzare un'interfaccia qui. L'ho cambiato.
Markus Schulte,

4

XStream offre una semplice utility per serializzare / deserializzare da / verso XML ed è molto veloce. La memorizzazione di CLOB XML anziché BLOB binari sarà meno fragile, per non dire più leggibile.



3

Se stai memorizzando un oggetto come dati binari nel database, dovresti davvero utilizzare un BLOBtipo di dati. Il database è in grado di memorizzarlo in modo più efficiente e non devi preoccuparti di codifiche e simili. JDBC fornisce metodi per la creazione e il recupero di BLOB in termini di flussi. Se possibile, usa Java 6, ha reso alcune aggiunte all'API JDBC che rendono molto più semplice la gestione dei BLOB.

Se hai assolutamente bisogno di archiviare i dati come stringa, consiglierei XStream per l'archiviazione basata su XML (molto più semplice di XMLEncoder), ma rappresentazioni di oggetti alternativi potrebbero essere altrettanto utili (ad esempio JSON). L'approccio dipende dal motivo per cui è effettivamente necessario archiviare l'oggetto in questo modo.


2

Dai un'occhiata alla classe java.sql.PreparedStatement, in particolare la funzione

http://java.sun.com/javase/6/docs/api/java/sql/PreparedStatement.html#setBinaryStream(int,%20java.io.InputStream)

Dai un'occhiata alla classe java.sql.ResultSet, in particolare la funzione

http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#getBinaryStream(int)

Tieni presente che se stai serializzando un oggetto in un database e poi cambi l'oggetto nel tuo codice in una nuova versione, il processo di deserializzazione può facilmente fallire perché la firma dell'oggetto è cambiata. Una volta ho commesso questo errore con l'archiviazione di una preferenza personalizzata serializzata e quindi con una modifica alla definizione delle preferenze. Improvvisamente non sono riuscito a leggere nessuna delle informazioni precedentemente serializzate.

Potrebbe essere meglio scrivere colonne ingombranti per proprietà in una tabella e comporre e decomporre l'oggetto in questo modo, invece di evitare questo problema con le versioni degli oggetti e la deserializzazione. Oppure scrivere le proprietà in un hashmap di qualche tipo, come un oggetto java.util.Properties, e quindi serializzare l'oggetto proprietà che è estremamente improbabile che cambi.


1

Il flusso serializzato è solo una sequenza di byte (ottetti). Quindi la domanda è come convertire una sequenza di byte in una stringa e viceversa. Inoltre deve utilizzare un set limitato di codici di caratteri se deve essere archiviato in un database.

La soluzione ovvia al problema è cambiare il campo in un LOB binario. Se vuoi restare con un LOB di caratteri, dovrai codificare in uno schema come base64, hex o uu.


1

È possibile utilizzare le classi build in sun.misc.Base64Decoder e sun.misc.Base64Encoder per convertire i dati binari della serializzazione in una stringa. Non sono necessarie ulteriori classi perché sono integrate.



0

Soluzione semplice, ha funzionato per me

public static byte[] serialize(Object obj) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(out);
    os.writeObject(obj);
    return out.toByteArray();
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.