オブジェクトを文字列にシリアル化する方法


149

次のコードスニペットに示すように、オブジェクトをファイルにシリアル化し、それを再度復元できます。オブジェクトを文字列にシリアル化し、代わりにデータベースに保存したいと思います。誰か助けてもらえますか?

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

回答:


269

セルジオ:

BLOBを使用する必要があります。JDBCを使用すると、非常に簡単です。

あなたが投稿した2番目のコードの問題はエンコーディングです。さらにバイトをエンコードして、失敗しないことを確認する必要があります。

それをStringに書き留めたい場合は、java.util.Base64を使用してバイトをエンコードできます。

それでも、シリアル化されたデータがどのくらい続くかわからないため、データ型としてCLOBを使用する必要があります。

使用方法のサンプルを以下に示します。

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

出力:

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

:Java 7以前の場合、元の回答はこちらで確認できます


本当に文字列が必要な場合は+ 1、base64 + clobが最適です。
ジョンガードナー

6
+1、小さな改善。toString()メソッドでプレーンオブジェクトの代わりにSerializableインターフェースを使用する方が良い:プライベート静的文字列toString(Serializableオブジェクト)
tefozi

4
オブジェクトを文字列ではなくバイト配列として格納しようとすると、BASE64を使用せずに同じことを実現できます。
Sudar

2
これの致命的な欠陥は、クラス定義が時間とともに変化する傾向があることです-そのような変更が発生した場合、デシリアライズできなくなります!serialVersionUIDto SomeClassを追加すると、新しいフィールドが追加されないように保護されますが、フィールドが削除された場合は、失敗します。効果的なJavaの本に関するJoshua Blochの発言を読む価値があります-books.google.co.uk/…–
Nick Holt

1
Java 8以降、java.util.Base64が追加されました。答えを更新する必要があります:Base64.getEncoder()。encodeToString(baos.toByteArray()); Base64.getDecoder()。decode(s);
drUniversalis

12

FileOutputStreamではなくByteArrayOutputStreamにデータを書き込むのはどうですか?

それ以外の場合は、XMLEncoderを使用してオブジェクトをシリアル化し、XMLを永続化してから、XMLDecoderを介して逆シリアル化できます。


8

素早い返信をありがとう。私はあなたの助けを認めるためにすぐにいくつかの賛成票を差し上げます。私はあなたの答えに基づいて私の意見で最良の解決策をコーディングしました。

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

なお、私はあまり効率的であるため、JSONを使用して考えられませんでした。

注:シリアル化されたオブジェクトをデータベースに文字列として格納するのではなく、byte []を格納することについてのアドバイスを検討します。


3
「ByteArrayOutputStream.toStringは、プラットフォームのデフォルトのエンコーディングを使用して変換します。それを実行してもよろしいですか?特に、任意のバイト配列は有効なUTF8ではないため、データベースによって変換されます。」
トム・ホーティン-タックライン2008

Tom Hawtinからの上記のコメントを真剣に受け止めてください
anjanb

シリアル化されたオブジェクトの長期保存は言うまでもなく素晴らしいアイデアであり、推奨されません
Steve g

「効率が悪いため、JSONの使用は考慮していません。」効率化のためにグーグルのプロトコルバッファを使用するのはどうですか?また、Steve gのアイデアは完全に理にかなっています。シリアル化されたデータをDBに格納し、Java以外の言語で使用できるようにする1つの方法は、プロトコルバッファーです。
anjanb 2008

4

XStreamは、XMLとのシリアライズ/デシリアライズのためのシンプルなユーティリティを提供し、非常に高速です。バイナリBLOBではなくXML CLOBを格納すると、より読みやすくなることは言うまでもなく、壊れにくくなります。



4

OscarRyzからの回答に触発された、Java8のアプローチ。ObjectをStringに変換します。デコーディング/エンコーディングでは、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();
    }
  }
}

なぜこれがクラスではなくインターフェイスなのですか?
simonalexander2005

@ simonalexander2005意味のあるメモ、ここではインターフェイスをもう使用しません。私はそれを変えた。
Markus Schulte

3

オブジェクトをバイナリデータとしてデータベースに格納する場合は、実際にはBLOBデータ型を使用する必要があります。データベースはそれをより効率的に格納することができ、エンコーディングなどについて心配する必要はありません。JDBCは、ストリームに関してBLOBを作成および取得するためのメソッドを提供します。可能であればJava 6を使用します。これにより、JDBC APIにいくつかの追加が行われ、BLOBの処理がずっと簡単になりました。

データを文字列として保存する必要がある場合は、XMLベースのストレージにXStreamを使用することをお勧めします(よりもはるかに簡単ですXMLEncoder)が、代わりのオブジェクト表現(JSONなど)も同様に役立つ場合があります。方法は、オブジェクトをこのように実際に格納する必要がある理由によって異なります。


2

java.sql.PreparedStatementクラス、特に関数を見てください。

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

次に、java.sql.ResultSetクラス、特に関数を見てください。

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

オブジェクトをデータベースにシリアル化している場合、新しいバージョンのコードでオブジェクトを変更すると、オブジェクトの署名が変更されたため、逆シリアル化プロセスが簡単に失敗する可能性があることに注意してください。シリアル化されたカスタム設定を保存して、設定定義に変更を加えることで、この間違いを犯したことがあります。突然、以前にシリアル化された情報を読み取ることができなくなりました。

オブジェクトのバージョンと逆シリアル化に関するこの問題を回避するには、テーブル内のプロパティ列ごとに不格好なものを記述し、代わりにこの方法でオブジェクトを構成および分解する方がよい場合があります。または、java.util.Propertiesオブジェクトのようなある種のハッシュマップにプロパティを書き込んでから、変更される可能性が非常に低いプロパティオブジェクトをシリアル化します。


1

シリアライズされたストリームは、バイト(オクテット)のシーケンスです。したがって、問題は、バイトのシーケンスを文字列に変換し、再び戻す方法です。さらに、データベースに格納する場合は、限られた文字コードのセットを使用する必要があります。

この問題の明らかな解決策は、フィールドをバイナリLOBに変更することです。文字LOBを使い続ける場合は、base64、hex、uuなどのスキームでエンコードする必要があります。


1

ビルドのクラスsun.misc.Base64Decoderおよびsun.misc.Base64Encoderを使用して、シリアライズのバイナリデータを文字列に変換できます。組み込みなので、追加のクラスは必要ありません。



0

私のために働いたシンプルなソリューション

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.