Bagaimana membuat cerita bersambung suatu objek menjadi sebuah string

150

Saya dapat membuat cerita bersambung suatu objek menjadi file dan kemudian mengembalikannya lagi seperti yang ditunjukkan pada cuplikan kode berikutnya. Saya ingin membuat cerita bersambung objek menjadi string dan menyimpannya ke dalam database. Ada yang bisa bantu saya?

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
sumber

Jawaban:

270

Sergio:

Anda harus menggunakan BLOB . Ini cukup lurus ke depan dengan JDBC.

Masalah dengan kode kedua yang Anda posting adalah pengkodean. Anda juga harus mengkodekan byte untuk memastikan tidak ada yang gagal.

Jika Anda masih ingin menuliskannya ke dalam String, Anda dapat menyandikan byte menggunakan java.util.Base64 .

Tetap Anda harus menggunakan CLOB sebagai tipe data karena Anda tidak tahu berapa lama data serial akan terjadi.

Berikut adalah contoh cara menggunakannya.

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

Keluaran:

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

CATATAN : untuk Java 7 dan sebelumnya Anda dapat melihat jawaban aslinya di sini

OscarRyz
sumber
+1 jika Anda BENAR-BENAR membutuhkan string, maka base64 + clob adalah cara untuk melakukannya.
John Gardner
6
+1, Peningkatan kecil. Lebih baik menggunakan antarmuka Serializable bukan Obyek polos di toString () metode: private static String toString (objek Serializable)
tefozi
4
Jika kita mencoba menyimpan objek sebagai array byte alih-alih string, maka kita dapat mencapai samething tanpa menggunakan BASE64.
Sudar
2
Kelemahan fatal dalam hal ini adalah bahwa definisi kelas cenderung berubah seiring waktu - jika perubahan seperti itu terjadi, Anda tidak akan dapat deserialisasi! Menambahkan serialVersionUIDke SomeClassakan melindungi terhadap bidang baru yang ditambahkan tetapi jika bidang dihapus Anda akan kacau. Layak untuk membaca apa yang dikatakan Joshua Bloch tentang ini di Java Efektif - books.google.co.id/...
Nick Holt
1
Sejak Java 8 sekarang ada java.util.Base64. Anda harus memperbarui jawaban Anda: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
drUniversalis
12

Bagaimana dengan menulis data ke ByteArrayOutputStream alih-alih FileOutputStream?

Jika tidak, Anda bisa membuat serial objek menggunakan XMLEncoder, tahan XML, lalu deserialize melalui XMLDecoder.

Programmer Hukum
sumber
8

Terima kasih atas balasan yang bagus dan cepat. Saya akan memberikan beberapa suara secara langsung untuk mengakui bantuan Anda. Saya telah memberi kode solusi terbaik menurut pendapat saya berdasarkan jawaban Anda.

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

Catatan saya tidak mempertimbangkan menggunakan JSON karena kurang efisien.

Catatan: Saya akan mempertimbangkan saran Anda tentang tidak menyimpan objek berseri sebagai string dalam database tetapi sebagai byte [].

Sergio del Amo
sumber
3
"ByteArrayOutputStream.toString mengkonversi menggunakan pengkodean platform default . Apakah Anda yakin itu? Terutama sebagai array byte yang sewenang-wenang tidak valid UTF8. Selanjutnya, database akan memotong-motongnya."
Tom Hawtin - tackline
Anda harus menanggapi komentar di atas dari Tom Hawtin dengan serius
anjanb
Belum lagi penyimpanan jangka panjang objek serial bukanlah ide bagus dan tidak disarankan
Steve g
"Catatan saya tidak mempertimbangkan menggunakan JSON karena kurang efisien." Bagaimana dengan menggunakan buffer protokol Google untuk efisiensi? Juga, ide Steve g sangat masuk akal. Satu cara untuk menyimpan data serial dalam DB namun membuatnya tersedia untuk bahasa selain java adalah lagi, buffer protokol.
anjanb
5

Pendekatan Java8, mengubah Object dari / ke String, terinspirasi oleh jawaban dari OscarRyz . Untuk de- / encoding, java.util.Base64 diperlukan dan digunakan.

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
sumber
Mengapa ini sebuah antarmuka daripada kelas?
simonalexander2005
@ simonalexander2005 Catatan yang bermakna, saya tidak akan menggunakan antarmuka lagi di sini. Saya mengubahnya.
Markus Schulte
4

XStream menyediakan utilitas sederhana untuk membuat serial / deserializing ke / dari XML, dan ini sangat cepat. Menyimpan XML CLOBs daripada BLOBS biner akan menjadi kurang rapuh, belum lagi lebih mudah dibaca.

skaffman
sumber
4

Bagaimana dengan bertahan objek sebagai gumpalan

Kristian
sumber
Perbaiki tautannya, ok?
3

Jika Anda menyimpan objek sebagai data biner dalam database, maka Anda harus menggunakan BLOBtipe data. Basis data dapat menyimpannya dengan lebih efisien, dan Anda tidak perlu khawatir tentang penyandian dan sejenisnya. JDBC menyediakan metode untuk membuat dan mengambil gumpalan dalam hal aliran. Gunakan Java 6 jika Anda bisa, itu membuat beberapa penambahan ke JDBC API yang membuat berurusan dengan gumpalan jauh lebih mudah.

Jika Anda benar-benar perlu menyimpan data sebagai String, saya akan merekomendasikan XStream untuk penyimpanan berbasis XML (jauh lebih mudah daripada XMLEncoder), tetapi representasi objek alternatif mungkin sama berguna (misalnya JSON). Pendekatan Anda tergantung pada mengapa Anda benar-benar perlu menyimpan objek dengan cara ini.

Daniel Spiewak
sumber
2

Lihatlah kelas java.sql.PreparedStatement, khususnya fungsinya

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

Kemudian lihatlah kelas java.sql.ResultSet, khususnya fungsinya

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

Ingatlah bahwa jika Anda membuat serial objek ke dalam database, dan kemudian Anda mengubah objek dalam kode Anda dalam versi baru, proses deserialisasi dapat dengan mudah gagal karena tanda tangan objek Anda berubah. Saya pernah melakukan kesalahan ini dengan menyimpan Preferensi kustom bersambung dan kemudian membuat perubahan ke definisi Preferensi. Tiba-tiba saya tidak bisa membaca informasi serial sebelumnya.

Anda mungkin lebih baik menulis kikuk per kolom properti dalam tabel dan menulis dan mendekomposisi objek dengan cara ini, untuk menghindari masalah ini dengan versi objek dan deserialization. Atau menulis properti ke dalam semacam peta, seperti objek java.util.Properties, dan kemudian membuat serial objek properti yang sangat tidak mungkin berubah.

Josh
sumber
1

Aliran berseri hanyalah urutan byte (oktet). Jadi pertanyaannya adalah bagaimana mengubah urutan byte ke String, dan kembali lagi. Lebih lanjut ia perlu menggunakan seperangkat kode karakter terbatas jika akan disimpan dalam database.

Solusi yang jelas untuk masalah ini adalah mengubah bidang ke LOB biner. Jika Anda ingin tetap menggunakan LOB characer, maka Anda harus menyandikan dalam beberapa skema seperti base64, hex atau uu.

Tom Hawtin - tackline
sumber
1

Anda dapat menggunakan build di kelas sun.misc.Base64Decoder dan sun.misc.Base64Encoder untuk mengonversi data biner dari serialisasi menjadi string. Anda tidak perlu kelas tambahan karena ini dibangun.


sumber
0

Anda dapat menggunakan UUEncoding

CiNN
sumber
0

Solusi Sederhana, berhasil untuk saya

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
sumber