So serialisieren Sie ein Objekt in eine Zeichenfolge

149

Ich kann ein Objekt in eine Datei serialisieren und dann wieder herstellen, wie im nächsten Codeausschnitt gezeigt. Ich möchte das Objekt in eine Zeichenfolge serialisieren und stattdessen in einer Datenbank speichern. Kann mir jemand helfen?

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
quelle

Antworten:

269

Sergio:

Sie sollten BLOB verwenden . Mit JDBC ist das ziemlich einfach.

Das Problem mit dem zweiten Code, den Sie veröffentlicht haben, ist die Codierung. Sie sollten die Bytes zusätzlich codieren, um sicherzustellen, dass keines von ihnen fehlschlägt.

Wenn Sie es dennoch in einen String schreiben möchten, können Sie die Bytes mit java.util.Base64 codieren .

Trotzdem sollten Sie CLOB als Datentyp verwenden, da Sie nicht wissen, wie lange die serialisierten Daten dauern werden.

Hier ist ein Beispiel für die Verwendung.

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

Ausgabe:

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

HINWEIS : Für Java 7 und frühere Versionen finden Sie hier die ursprüngliche Antwort

OscarRyz
quelle
+1 Wenn Sie WIRKLICH Strings benötigen, ist base64 + clob der richtige Weg.
John Gardner
6
+1, kleine Verbesserung. Verwenden Sie in der toString () -Methode besser die Schnittstelle Serializable anstelle von
normalem
4
Wenn wir versuchen, das Objekt als Byte-Array anstelle eines Strings zu speichern, können wir das Gleiche erreichen, ohne BASE64 zu verwenden.
Sudar
2
Der fatale Fehler dabei ist, dass sich Klassendefinitionen im Laufe der Zeit ändern - wenn eine solche Änderung auftritt, können Sie nicht deserialisieren! Das Hinzufügen eines serialVersionUIDto SomeClassschützt vor dem Hinzufügen neuer Felder. Wenn jedoch Felder entfernt werden, werden Sie geschraubt. Es lohnt sich zu lesen, was Joshua Bloch dazu in Effective Java zu sagen hat - books.google.co.uk/…
Nick Holt
1
Seit Java 8 gibt es jetzt java.util.Base64. Sie sollten Ihre Antwort aktualisieren: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
drUniversalis
12

Wie wäre es, wenn Sie die Daten in einen ByteArrayOutputStream anstelle eines FileOutputStream schreiben?

Andernfalls können Sie das Objekt mit XMLEncoder serialisieren, das XML beibehalten und dann über XMLDecoder deserialisieren.

Outlaw Programmer
quelle
8

Vielen Dank für tolle und schnelle Antworten. Ich werde sofort einige Stimmen abgeben, um Ihre Hilfe zu bestätigen. Ich habe meiner Meinung nach die beste Lösung basierend auf Ihren Antworten codiert.

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

Hinweis: Ich habe nicht in Betracht gezogen, JSON zu verwenden, da es weniger effizient ist.

Hinweis: Ich werde Ihren Rat berücksichtigen, nicht serialisierte Objekte als Zeichenfolgen in der Datenbank zu speichern, sondern stattdessen Byte [].

Sergio del Amo
quelle
3
"ByteArrayOutputStream.toString konvertiert unter Verwendung der Plattform-Standardcodierung . Sind Sie sicher, dass Sie das möchten? Zumal ein beliebiges Byte-Array nicht gültig ist UTF8. Außerdem wird die Datenbank es entstellen."
Tom Hawtin - Tackline
Sie sollten den obigen Kommentar von Tom Hawtin ernst nehmen
anjanb
Ganz zu schweigen von der Langzeitspeicherung von serialisierten Objekten ist keine gute Idee und wird nicht empfohlen
Steve g
"Beachten Sie, dass ich JSON nicht in Betracht gezogen habe, weil es weniger effizient ist." Wie wäre es mit der Verwendung der Protokollpuffer von Google aus Effizienzgründen? Auch die Idee von Steve g macht durchaus Sinn. Eine Möglichkeit, serialisierte Daten in einer Datenbank zu speichern und sie dennoch für andere Sprachen als Java verfügbar zu machen, sind wiederum Protokollpuffer.
Anjanb
4

XStream bietet ein einfaches Dienstprogramm zum Serialisieren / Deserialisieren in / von XML und ist sehr schnell. Das Speichern von XML-CLOBs anstelle von binären BLOBS wird weniger anfällig sein, ganz zu schweigen von der besseren Lesbarkeit.

Skaffman
quelle
4

Java8-Ansatz, der das Objekt von / nach String konvertiert, inspiriert von der Antwort von OscarRyz . Für die De- / Codierung wird java.util.Base64 benötigt und verwendet.

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
quelle
Warum ist dies eher eine Schnittstelle als eine Klasse?
Simonalexander2005
@ simonalexander2005 Sinnvoller Hinweis, ich würde hier keine Schnittstelle mehr verwenden. Ich habe es geändert.
Markus Schulte
3

Wenn Sie ein Objekt als Binärdaten in der Datenbank speichern, sollten Sie wirklich einen BLOBDatentyp verwenden. Die Datenbank kann es effizienter speichern, und Sie müssen sich nicht um Codierungen und dergleichen kümmern. JDBC bietet Methoden zum Erstellen und Abrufen von Blobs in Form von Streams. Verwenden Sie Java 6, wenn Sie können. Es wurden einige Ergänzungen an der JDBC-API vorgenommen, die den Umgang mit Blobs erheblich vereinfachen.

Wenn Sie die Daten unbedingt als Zeichenfolge speichern müssen, würde ich XStream für die XML-basierte Speicherung empfehlen (viel einfacher als XMLEncoder), aber alternative Objektdarstellungen sind möglicherweise genauso nützlich (z. B. JSON). Ihr Ansatz hängt davon ab, warum Sie das Objekt tatsächlich auf diese Weise speichern müssen.

Daniel Spiewak
quelle
2

Schauen Sie sich die Klasse java.sql.PreparedStatement an, insbesondere die Funktion

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

Schauen Sie sich dann die Klasse java.sql.ResultSet an, insbesondere die Funktion

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

Beachten Sie, dass der Deserialisierungsprozess leicht fehlschlagen kann, wenn Sie ein Objekt in eine Datenbank serialisieren und dann das Objekt in Ihrem Code in einer neuen Version ändern, da sich die Signatur Ihres Objekts geändert hat. Ich habe diesen Fehler einmal gemacht, indem ich benutzerdefinierte Einstellungen gespeichert und dann die Definition der Einstellungen geändert habe. Plötzlich konnte ich keine der zuvor serialisierten Informationen mehr lesen.

Es ist möglicherweise besser, klobige Spalten pro Eigenschaft in eine Tabelle zu schreiben und das Objekt stattdessen auf diese Weise zu komponieren und zu zerlegen, um dieses Problem mit Objektversionen und Deserialisierung zu vermeiden. Oder schreiben Sie die Eigenschaften in eine Hashmap wie ein java.util.Properties-Objekt und serialisieren Sie dann das Eigenschaftenobjekt, dessen Änderung äußerst unwahrscheinlich ist.

Josh
quelle
1

Der serialisierte Stream ist nur eine Folge von Bytes (Oktetten). Die Frage ist also, wie eine Folge von Bytes in einen String und wieder zurück konvertiert werden kann. Außerdem muss ein begrenzter Satz von Zeichencodes verwendet werden, wenn es in einer Datenbank gespeichert werden soll.

Die offensichtliche Lösung des Problems besteht darin, das Feld in ein binäres LOB zu ändern. Wenn Sie bei einem Zeichen-LOB bleiben möchten, müssen Sie in einem Schema wie base64, hex oder uu codieren.

Tom Hawtin - Tackline
quelle
1

Sie können die eingebauten Klassen sun.misc.Base64Decoder und sun.misc.Base64Encoder verwenden, um die Binärdaten der Serialisierung in eine Zeichenfolge zu konvertieren. Sie benötigen keine zusätzlichen Klassen, da diese integriert sind.


quelle
0

Sie können UUEncoding verwenden

CiNN
quelle
0

Einfache Lösung, hat bei mir funktioniert

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
quelle