概要

pythonでシリアライズとデシリアライズ を行った際の、デシリアライズされる環境下で定義内容が変わっている場合の挙動が気になったので Java でもやってみた。
※基本的なシリアライズ/デシリアライズの実装は、以前に書いた シリアライズとデシリアライズ の内容とほぼ同じ。
 (違いは Base64エンコード/デコード にguavaを使用している事ぐらい)

目次

プロジェクトの作成

作業用フォルダ作成

mkdir serialize_java
cd serialize_java

Javaプロジェクト作成

gradle init --type java-application

不要なファイルを削除

rm -rf src/test/java/*

いちおう build.gradle の内容も貼っておく。
※何も変更していない。(表示用にコメント削除しただけ)

build.gradle

plugins {
    id 'java'
    id 'application'
}

mainClassName = 'App'

dependencies {
    compile 'com.google.guava:guava:23.0'
    testCompile 'junit:junit:4.12'
}

repositories {
    jcenter()
}

まずは普通にシリアライズとデシリアライズ

まずは、普通にシリアライズとデシリアライズを実行する処理を作成していく。

最初にシリアライズ/デシリアライズを行う為の共通処理を作成。
※以前に書いた シリアライズとデシリアライズ の内容とほぼ同じ。(違いは Base64エンコード/デコード にguavaを使用している事ぐらい)

src/main/java/sample/MySerializer.java

package sample;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import com.google.common.io.BaseEncoding;

public class MySerializer {
    public static String serialize(Object obj) throws IOException{
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        try {
            objectOutputStream.writeObject(obj);
            ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream)outputStream;
            return BaseEncoding.base64().encode(byteArrayOutputStream.toByteArray());
        } catch (Exception e){ 
            e.printStackTrace();
        } finally {
            objectOutputStream.close();
            outputStream.close();
        }   
        return null;
    }   

    public static Object deSerialize(String encodedText) throws IOException, ClassNotFoundException{
        byte[] decoded =  BaseEncoding.base64().decode(URLDecoder.decode(encodedText, "UTF-8"));
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(decoded));
        return objectInputStream.readObject();
    }   
}

次は、シリアライズ/デシリアライズされるクラス。
※1つの属性と1つのメソッドがあるだけのシンプルなクラス。
※getter、setter 書くのがメンドかったので、属性も public にしている。
※忘れずに Serializable を implements しておく。

src/main/java/sample/MyModule1.java

package sample;

import java.io.Serializable;

public class MyModule1 implements Serializable {
    private static final long serialVersionUID = 1L; 
    public String var1 = "A";
    public void print(){
        System.out.println("var1: " + this.var1);
    }   
}

最後にメイン処理。

src/main/java/App.java

import sample.MyModule1;
import sample.MySerializer;

public class App {
    public static void main(String[] args) {
        MyModule1 module1 = new MyModule1();
        module1.var1 = "AAA";
        try {
            String serializedText = MySerializer.serialize(module1);
            System.out.println("serializedText: " + serializedText);
            MyModule1 ret = (MyModule1)MySerializer.deSerialize(serializedText);
            ret.print();
        } catch (Exception e) {
            e.printStackTrace();
        }   
    }   
}

いちおう作成したファイルの状態を貼っておく。

$ tree src
src
├── main
│   └── java
│       ├── App.java
│       └── sample
│           ├── MyModule1.java
│           └── MySerializer.java
└── test
    └── java

シリアライズ 及び デシリアライズを実行してみる。

$ gradle run

> Task :run
serializedText: rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE=
var1: AAA

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

まずは、正常に実行できる事を確認。

デシリアライズされるオブジェクトの定義がない場合

MyModule1 を削除(いったんリネーム)

mv src/main/java/sample/MyModule1.java src/main/java/sample/MyModule1.txt

メイン処理を以下の通り変更。

src/main/java/App.java

/* コメントアウト
import sample.MyModule1;
*/
import sample.MySerializer;

public class App {
    public static void main(String[] args) {
        //MyModule1 module1 = new MyModule1();  // コメントアウト
        //module1.var1 = "AAA";                 // コメントアウト
        try {
            /* コメントアウト
            String serializedText = MySerializer.serialize(module1);
            */
            // 追加 (さきほど実行した時に表示された文字列をコピペ)
            String serializedText = "rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE=";
            System.out.println("serializedText: " + serializedText);
            /* コメントアウト
            MyModule1 ret = (MyModule1)MySerializer.deSerialize(serializedText);
            ret.print();
            */
            // 以下2行追加
            Object obj = MySerializer.deSerialize(serializedText);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }   
    }   
}

デシリアライズを実行してみる。
※ build/classes 配下に先程ビルドした MyModule1 が残っている為、clean で削除してから実行する。

gradle clean

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

gradle run

> Task :run
serializedText: rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE=
java.lang.ClassNotFoundException: sample.MyModule1
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:398)
        at java.base/java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:693)
        at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1886)
        at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
        at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
        at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
        at App.deSerialize(App.java:50)
        at App.main(App.java:24)

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

やはり対象オブジェクトの定義がないとエラーになる。
※ Python での挙動と同じ。( pythonでシリアライズとデシリアライズ )

対象オブジェクトの定義内容が変更されている場合

次はオブジェクトの定義内容が変更されている場合の挙動を見ていく。

各ソースを以下の通り変更。

src/main/java/sample/MyModule1.java

package sample;

import java.io.Serializable;

public class MyModule1 implements Serializable {
    private static final long serialVersionUID = 1L; 
    public String var1 = "A";
    public void print(){
        System.out.println("var1 is " + this.var1 + "!!");  // メソッドの内容を変更
    }
    public void display(){
        System.out.println("var1 は " + this.var1);          // メソッドを追加
    }
}

src/main/java/App.java

import sample.MyModule1;
import sample.MySerializer;

public class App {
    public static void main(String[] args) {
        try {
            String serializedText = "rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE=";
            System.out.println("serializedText: " + serializedText);
            MyModule1 ret = (MyModule1)deSerialize(serializedText);
            ret.print();
            ret.display(); // 追加したメソッドを呼んでみる
        } catch (Exception e) {
            e.printStackTrace();
        }   
    }
}

ビルド済みの classファイルをいったん削除して、再実行。

$ gradle clean

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

$ gradle run

> Task :run
serializedText: rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE=
var1 is AAA!!
var1 は AAA

BUILD SUCCESSFUL in 0s
2 actionable tasks: 2 executed

やはり、メソッドの定義までは復元されない模様。
pythonでシリアライズとデシリアライズ と同じ結果。


トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2020-01-12 (日) 02:49:43 (1559d)