Como serializar um objeto em uma string

150

Sou capaz de serializar um objeto em um arquivo e restaurá-lo novamente, como é mostrado no próximo trecho de código. Gostaria de serializar o objeto em uma string e armazenar em um banco de dados. Alguém pode me ajudar?

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();
Sergio del Amo
fonte

Respostas:

270

Sergio:

Você deve usar BLOB . É bem direto com o JDBC.

O problema com o segundo código que você postou é a codificação. Além disso, você deve codificar os bytes para garantir que nenhum deles falhe.

Se você ainda deseja anotá-lo em uma String, pode codificar os bytes usando java.util.Base64 .

Ainda assim, você deve usar CLOB como tipo de dados, porque não sabe quanto tempo os dados serializados serão.

Aqui está uma amostra de como usá-lo.

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;
    }
}

Resultado:

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 : para Java 7 e versões anteriores, você pode ver a resposta original aqui

OscarRyz
fonte
+1 se você realmente precisar de strings, então o base64 + clob é o caminho a percorrer.
John Gardner
6
+1, pequena melhoria. Melhor usar interface Serializable em vez de objetos simples no método toString (): private static string toString (Serializable objeto)
tefozi
4
Se tentarmos armazenar o objeto como uma matriz de bytes em vez de uma string, poderemos obter a mesma coisa sem usar o BASE64.
Sudar
2
A falha fatal disso é que as definições de classe tendem a mudar ao longo do tempo - se essa mudança ocorrer, você não poderá desserializar! Adicionar um serialVersionUIDa SomeClassprotegerá contra novos campos sendo adicionados, mas se os campos forem removidos, você será ferrado. Vale a pena ler o que Joshua Bloch tem a dizer sobre isso em Java eficaz - books.google.co.uk/…
Nick Holt
1
Desde o Java 8, agora existe o java.util.Base64. Você deve atualizar sua resposta: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
DrUniversalis
12

Que tal gravar os dados em um ByteArrayOutputStream em vez de em um FileOutputStream?

Caso contrário, você pode serializar o objeto usando XMLEncoder, persistir o XML e desserializar via XMLDecoder.

Programador foragido
fonte
8

Obrigado por respostas excelentes e rápidas. Darei alguns votos imediatamente para agradecer sua ajuda. Codifiquei a melhor solução na minha opinião, com base nas suas respostas.

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();
}

Observe que eu não considerei usar JSON porque é menos eficiente.

Nota: Considerarei seu conselho sobre não armazenar objetos serializados como seqüências de caracteres no banco de dados, mas como byte [].

Sergio del Amo
fonte
3
"ByteArrayOutputStream.toString se converte usando a codificação padrão da plataforma . Tem certeza de que deseja isso? Particularmente, como uma matriz de bytes arbitrária não é um UTF8 válido. Além disso, o banco de dados irá alterá-lo."
Tom Hawtin - defina
Você deve ter o comentário acima de Tom Hawtin sério
anjanb
Para não mencionar a longo prazo de armazenamento de objetos serializados não é uma grande idéia e não recomendado
Steve g
"Note que eu não considerei usar JSON porque é menos eficiente." Que tal usar buffers de protocolo do Google para obter eficiência? Além disso, a ideia de Steve g faz todo o sentido. Uma maneira de armazenar dados serializados em um banco de dados e disponibilizá-los para outros idiomas além do java é novamente, os buffers de protocolo.
anjanb 25/09/08
5

Abordagem Java8, convertendo Object de / para String, inspirada na resposta de OscarRyz . Para descodificação / codificação, java.util.Base64 é necessário e usado.

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();
    }
  }
}
Markus Schulte
fonte
Por que isso é uma interface e não uma classe?
simonalexander2005
@ simonalexander2005 Nota significativa: eu não usaria mais uma interface aqui. Eu mudei isso.
Markus Schulte
4

O XStream fornece um utilitário simples para serializar / desserializar de / para XML, e é muito rápido. Armazenar CLOBs XML em vez de BLOBS binários será menos frágil, sem mencionar mais legível.

skaffman
fonte
4

Que tal persistir o objeto como um blob

Kristian
fonte
Corrigir o link, ok?
3

Se você estiver armazenando um objeto como dados binários no banco de dados, deverá realmente usar um BLOBtipo de dados. O banco de dados é capaz de armazená-lo com mais eficiência, e você não precisa se preocupar com codificações e similares. O JDBC fornece métodos para criar e recuperar blobs em termos de fluxos. Use o Java 6, se puder, ele fez algumas adições à API JDBC que tornam muito mais fácil lidar com blobs.

Se você absolutamente precisar armazenar os dados como uma String, eu recomendaria o XStream para armazenamento baseado em XML (muito mais fácil que XMLEncoder), mas representações de objetos alternativas podem ser igualmente úteis (por exemplo, JSON). Sua abordagem depende do motivo pelo qual você realmente precisa armazenar o objeto dessa maneira.

Daniel Spiewak
fonte
2

Dê uma olhada na classe java.sql.PreparedStatement, especificamente na função

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

Dê uma olhada na classe java.sql.ResultSet, especificamente na função

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

Lembre-se de que, se você estiver serializando um objeto em um banco de dados e depois alterar o objeto no seu código em uma nova versão, o processo de desserialização poderá falhar facilmente porque a assinatura do seu objeto foi alterada. Uma vez cometi esse erro ao armazenar Preferências personalizadas serializadas e depois fazer uma alteração na definição de Preferências. De repente, não consegui ler nenhuma informação serializada anteriormente.

Talvez seja melhor escrever colunas desajeitadas por propriedade em uma tabela e compor e decompor o objeto dessa maneira, para evitar esse problema nas versões e desserialização de objetos. Ou grave as propriedades em um hashmap de algum tipo, como um objeto java.util.Properties e, em seguida, serialize o objeto de propriedades que é extremamente improvável de mudar.

Josh
fonte
1

O fluxo serializado é apenas uma sequência de bytes (octetos). Portanto, a questão é como converter uma sequência de bytes em uma String e vice-versa. Além disso, ele precisa usar um conjunto limitado de códigos de caracteres para ser armazenado em um banco de dados.

A solução óbvia para o problema é alterar o campo para um LOB binário. Se você deseja manter um LOB de caractere, precisará codificar em algum esquema como base64, hex ou uu.

Tom Hawtin - linha de orientação
fonte
1

Você pode usar a construção nas classes sun.misc.Base64Decoder e sun.misc.Base64Encoder para converter os dados binários da serialização em uma cadeia de caracteres. Você não precisa de classes adicionais porque elas são incorporadas.


fonte
0

você pode usar UUEncoding

CiNN
fonte
0

Solução simples, funcionou para mim

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