Cómo serializar un objeto en una cadena

150

Puedo serializar un objeto en un archivo y luego restaurarlo nuevamente como se muestra en el siguiente fragmento de código. Me gustaría serializar el objeto en una cadena y almacenarlo en una base de datos. ¿Alguien puede ayudarme?

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
fuente

Respuestas:

270

Sergio:

Deberías usar BLOB . Es bastante sencillo con JDBC.

El problema con el segundo código que publicaste es la codificación. Además, debe codificar los bytes para asegurarse de que ninguno de ellos falle.

Si aún desea escribirlo en una Cadena, puede codificar los bytes usando java.util.Base64 .

Aún así, debe usar CLOB como tipo de datos porque no sabe cuánto tiempo durarán los datos serializados.

Aquí hay una muestra de cómo 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;
    }
}

Salida:

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 y versiones anteriores, puede ver la respuesta original aquí

OscarRyz
fuente
+1 si REALMENTE necesita cadenas, entonces base64 + clob es el camino a seguir.
John Gardner
66
+1, pequeña mejora. Mejor usar la interfaz Serializable en lugar de Objeto simple en el método toString (): String toString (objeto serializable) privado estático
tefozi
44
Si intentamos almacenar el objeto como una matriz de bytes en lugar de una cadena, entonces podemos lograr lo mismo sin usar BASE64.
Sudar
2
El defecto fatal en esto es que las definiciones de clase tienden a cambiar con el tiempo: si se produce dicho cambio, ¡no podrá deserializarse! Agregar un serialVersionUIDa SomeClassprotegerá contra nuevos campos que se agregarán, pero si se eliminan los campos, se perderá. Vale la pena leer lo que Joshua Bloch tiene que decir sobre esto en Java efectivo - books.google.co.uk/…
Nick Holt
1
Desde Java 8 ahora hay java.util.Base64. Debe actualizar su respuesta: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decodificación (es);
drUniversalis
12

¿Qué tal escribir los datos en un ByteArrayOutputStream en lugar de un FileOutputStream?

De lo contrario, puede serializar el objeto utilizando XMLEncoder, conservar el XML y luego deserializarlo mediante XMLDecoder.

Programador ilegal
fuente
8

Gracias por sus excelentes y rápidas respuestas. Daré algunos votos inmediatamente para agradecer su ayuda. En mi opinión, he codificado la mejor solución basada en sus respuestas.

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

Tenga en cuenta que no consideré usar JSON porque es menos eficiente.

Nota: consideraré su consejo sobre no almacenar objetos serializados como cadenas en la base de datos, sino el byte [].

Sergio del Amo
fuente
3
"ByteArrayOutputStream.toString convierte utilizando la codificación predeterminada de la plataforma . ¿Está seguro de que quiere eso? Particularmente como un conjunto de bytes arbitrario no es válido UTF8. Además, la base de datos lo va a destrozar".
Tom Hawtin - tackline
Deberías tomar en serio el comentario anterior de Tom Hawtin
anjanb
Sin mencionar que el almacenamiento a largo plazo de objetos serializados no es una gran idea y no se recomienda
Steve g
"Tenga en cuenta que no consideré usar JSON porque es menos eficiente". ¿Qué tal usar los buffers de protocolo de Google para mayor eficiencia? Además, la idea de Steve g tiene mucho sentido. Una forma de almacenar datos serializados en una base de datos y, sin embargo, hacer que estén disponibles para otros idiomas que no sean Java es, de nuevo, los búferes de protocolo.
anjanb
5

Enfoque Java8, que convierte Objeto de / a Cadena, inspirado en la respuesta de OscarRyz . Para la descodificación / codificación, se requiere y utiliza 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();
    }
  }
}
Markus Schulte
fuente
¿Por qué es esto una interfaz en lugar de una clase?
simonalexander2005
@ simonalexander2005 Nota significativa, ya no usaría una interfaz aquí. Lo cambie.
Markus Schulte
4

XStream proporciona una sencilla utilidad para serializar / deserializar a / desde XML, y es muy rápido. Almacenar CLOB XML en lugar de BLOBS binarios será menos frágil, por no mencionar más legible.

skaffman
fuente
4

¿Qué tal persistir el objeto como una gota

Kristian
fuente
Arregla el enlace, ok?
3

Si está almacenando un objeto como datos binarios en la base de datos, entonces realmente debería usar un BLOBtipo de datos. La base de datos puede almacenarla de manera más eficiente, y no tiene que preocuparse por las codificaciones y similares. JDBC proporciona métodos para crear y recuperar blobs en términos de flujos. Utilice Java 6 si puede, hizo algunas adiciones a la API JDBC que hacen que manejar blobs sea mucho más fácil.

Si realmente necesita almacenar los datos como una Cadena, recomendaría XStream para el almacenamiento basado en XML (mucho más fácil que XMLEncoder), pero las representaciones de objetos alternativas podrían ser igual de útiles (por ejemplo, JSON). Su enfoque depende de por qué realmente necesita almacenar el objeto de esta manera.

Daniel Spiewak
fuente
2

Eche un vistazo a la clase java.sql.PreparedStatement, específicamente la función

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

Luego eche un vistazo a la clase java.sql.ResultSet, específicamente la función

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

Tenga en cuenta que si está serializando un objeto en una base de datos, y luego cambia el objeto en su código en una nueva versión, el proceso de deserialización puede fallar fácilmente porque la firma de su objeto cambió. Una vez cometí este error al almacenar una Preferencia personalizada serializada y luego hacer un cambio en la definición de Preferencias. De repente no pude leer ninguna de las informaciones serializadas previamente.

Puede que sea mejor escribir columnas torpes por propiedad en una tabla y componer y descomponer el objeto de esta manera, para evitar este problema con las versiones de objetos y la deserialización. O escribir las propiedades en un hashmap de algún tipo, como un objeto java.util.Properties, y luego serializar el objeto de propiedades que es extremadamente improbable que cambie.

Josh
fuente
1

La secuencia serializada es solo una secuencia de bytes (octetos). Entonces, la pregunta es cómo convertir una secuencia de bytes en una Cadena y viceversa. Además, necesita usar un conjunto limitado de códigos de caracteres si va a almacenarse en una base de datos.

La solución obvia al problema es cambiar el campo a un LOB binario. Si desea seguir con un LOB de caracter, entonces necesitará codificar en algún esquema como base64, hexadecimal o uu.

Tom Hawtin - tackline
fuente
1

Puede usar la compilación en las clases sun.misc.Base64Decoder y sun.misc.Base64Encoder para convertir los datos binarios de la serialización en una cadena. No necesita clases adicionales porque están integradas.


fuente
0

puedes usar UUEncoding

CiNN
fuente
0

Solución simple, funcionó para mí

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
fuente