Jak serializować obiekt w ciąg

150

Jestem w stanie serializować obiekt do pliku, a następnie przywrócić go ponownie, jak pokazano w następnym fragmencie kodu. Chciałbym serializować obiekt w ciąg i zamiast tego przechowywać w bazie danych. Czy ktoś może mi pomóc?

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
źródło

Odpowiedzi:

270

Sergio:

Powinieneś użyć BLOBa . Z JDBC jest to całkiem proste.

Problem z drugim opublikowanym kodem dotyczy kodowania. Powinieneś dodatkowo zakodować bajty, aby upewnić się, że żaden z nich nie zawiedzie.

Jeśli nadal chcesz zapisać go w łańcuchu, możesz zakodować bajty za pomocą java.util.Base64 .

Nadal powinieneś używać CLOB jako typu danych, ponieważ nie wiesz, jak długo będą zserializowane dane.

Oto przykład, jak go używać.

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

Wynik:

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

UWAGA : w przypadku Java 7 i wcześniejszych oryginalną odpowiedź można znaleźć tutaj

OscarRyz
źródło
+1 jeśli NAPRAWDĘ potrzebujesz strun, to base64 + clob jest drogą do zrobienia.
John Gardner,
6
+1, mała poprawa. Lepiej używać interfejsu Serializable zamiast zwykłego obiektu w metodzie toString (): prywatny statyczny ciąg toString (obiekt Serializable)
tefozi
4
Jeśli spróbujemy zapisać obiekt jako tablicę bajtów zamiast łańcucha, możemy osiągnąć to samo bez użycia BASE64.
Sudar
2
Fatalnym błędem jest to, że definicje klas zmieniają się w czasie - jeśli taka zmiana nastąpi, nie będzie można deserializować! Dodanie serialVersionUIDdo SomeClassochroni przed dodawaniem nowych pól, ale jeśli pola zostaną usunięte, będziesz miał wkręty. Warto przeczytać, co na ten temat ma do powiedzenia Joshua Bloch w Effective Java - books.google.co.uk/…
Nick Holt
1
Od wersji Java 8 istnieje teraz java.util.Base64. Powinieneś zaktualizować swoją odpowiedź: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
drUniversalis
12

Co powiesz na zapisanie danych do ByteArrayOutputStream zamiast do FileOutputStream?

W przeciwnym razie możesz serializować obiekt za pomocą XMLEncoder, utrwalić XML, a następnie deserializować za pomocą XMLDecoder.

Wyjęty spod prawa programista
źródło
8

Dzięki za świetne i szybkie odpowiedzi. Natychmiast zrezygnuję z niektórych głosów, aby podziękować za pomoc. Zakodowałem najlepsze moim zdaniem rozwiązanie na podstawie Twoich odpowiedzi.

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

Uwaga: nie rozważałem używania JSON, ponieważ jest mniej wydajny.

Uwaga: Rozważę twoją radę, aby nie przechowywać zserializowanego obiektu jako ciągów w bazie danych, ale zamiast tego jako bajt [].

Sergio del Amo
źródło
3
„ByteArrayOutputStream.toString konwertuje przy użyciu domyślnego kodowania platformy . Czy na pewno chcesz?
Tom Hawtin - tackline
Powinieneś poważnie potraktować powyższy komentarz Toma Hawtina
anjanb
Nie wspominając o długoterminowym przechowywaniu zserializowanych obiektów nie jest świetnym pomysłem i nie jest zalecane
Steve g
„Uwaga: nie rozważałem używania formatu JSON, ponieważ jest mniej wydajny”. A co powiesz na wykorzystanie buforów protokołów Google w celu zwiększenia wydajności? Pomysł Steve'a g ma również sens. Jednym ze sposobów przechowywania zserializowanych danych w bazie danych, a jednocześnie udostępnienia ich dla języków innych niż java, są bufory protokołów.
anjanb
5

Podejście Java8, konwertowanie Object z / na String, zainspirowane odpowiedzią z OscarRyz . Do dekodowania / kodowania wymagany jest i używany plik 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
źródło
Dlaczego jest to interfejs, a nie klasa?
simonalexander 2005
@ simonalexander2005 Znacząca uwaga, nie użyłbym już tutaj interfejsu. Zmieniłem to.
Markus Schulte
4

XStream zapewnia proste narzędzie do serializacji / deserializacji do / z XML i jest bardzo szybkie. Przechowywanie XML CLOB zamiast binarnych BLOBS będzie mniej kruche, nie wspominając o bardziej czytelnym.

skaffman
źródło
4

Co powiesz na utrwalanie obiektu jako obiektu blob

Kristian
źródło
Napraw link, ok?
3

Jeśli przechowujesz obiekt jako dane binarne w bazie danych, to naprawdę powinieneś użyć BLOBtypu danych. Baza danych jest w stanie przechowywać ją wydajniej i nie musisz martwić się o kodowanie i tym podobne. JDBC udostępnia metody tworzenia i pobierania obiektów blob w postaci strumieni. Jeśli możesz, korzystaj z języka Java 6, ponieważ wprowadził on pewne dodatki do interfejsu API JDBC, które znacznie ułatwiają obsługę obiektów blob.

Jeśli absolutnie potrzebujesz przechowywać dane jako String, poleciłbym XStream do przechowywania opartego na XML (znacznie łatwiejsze niż XMLEncoder), ale alternatywne reprezentacje obiektów mogą być równie przydatne (np. JSON). Twoje podejście zależy od tego, dlaczego faktycznie musisz przechowywać obiekt w ten sposób.

Daniel Śpiewak
źródło
2

Przyjrzyj się klasie java.sql.PreparedStatement, a konkretnie funkcji

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

Następnie przyjrzyj się klasie java.sql.ResultSet, a konkretnie funkcji

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

Pamiętaj, że jeśli serializujesz obiekt do bazy danych, a następnie zmienisz obiekt w kodzie w nowej wersji, proces deserializacji może łatwo zakończyć się niepowodzeniem, ponieważ zmieniła się sygnatura obiektu. Kiedyś popełniłem ten błąd, przechowując niestandardowe serializowane preferencje, a następnie wprowadzając zmiany w definicji preferencji. Nagle nie mogłem odczytać żadnej z wcześniej zserializowanych informacji.

Lepszym rozwiązaniem może być pisanie niezgrabnych kolumn właściwości w tabeli oraz komponowanie i dekomponowanie obiektu w ten sposób, aby uniknąć tego problemu z wersjami obiektów i deserializacją. Lub zapisanie właściwości w jakiejś tablicy hashmapy, takiej jak obiekt java.util.Properties, a następnie serializacja obiektu properties, co jest bardzo mało prawdopodobne, aby się zmienił.

Josh
źródło
1

Zserializowany strumień to po prostu sekwencja bajtów (oktetów). Zatem pytanie brzmi, jak przekonwertować sekwencję bajtów na łańcuch i z powrotem. Ponadto musi użyć ograniczonego zestawu kodów znaków, jeśli ma być przechowywany w bazie danych.

Oczywistym rozwiązaniem problemu jest zmiana pola na binarny LOB. Jeśli chcesz trzymać się znaku LOB, musisz zakodować w jakimś schemacie, takim jak base64, hex lub uu.

Tom Hawtin - haczyk
źródło
1

Możesz użyć kompilacji w klasach sun.misc.Base64Decoder i sun.misc.Base64Encoder, aby przekonwertować dane binarne serializacji na ciąg. Nie potrzebujesz dodatkowych klas, ponieważ są wbudowane.


źródło
0

Proste rozwiązanie, zadziałało dla mnie

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
źródło