概要 †pythonでシリアライズとデシリアライズ を行った際の、デシリアライズされる環境下で定義内容が変わっている場合の挙動が気になったので Java でもやってみた。 目次 †プロジェクトの作成 †作業用フォルダ作成 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() } まずは普通にシリアライズとデシリアライズ †まずは、普通にシリアライズとデシリアライズを実行する処理を作成していく。 最初にシリアライズ/デシリアライズを行う為の共通処理を作成。 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(); } } 次は、シリアライズ/デシリアライズされるクラス。 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(); } } } デシリアライズを実行してみる。 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 やはり対象オブジェクトの定義がないとエラーになる。 対象オブジェクトの定義内容が変更されている場合 †次はオブジェクトの定義内容が変更されている場合の挙動を見ていく。 各ソースを以下の通り変更。 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 やはり、メソッドの定義までは復元されない模様。 |