#mynavi(Javaメモ) #setlinebreak(on); * 概要 [#lfa8cca7] #html(<div class="pl10">) [[pythonでシリアライズとデシリアライズ]] を行った際の、デシリアライズされる環境下で定義内容が変わっている場合の挙動が気になったので Java でもやってみた。 ※基本的なシリアライズ/デシリアライズの実装は、以前に書いた [[シリアライズとデシリアライズ]] の内容とほぼ同じ。 (違いは Base64エンコード/デコード にguavaを使用している事ぐらい) #html(</div>) * 目次 [#s93abf40] #contents - 関連 -- [[pythonでシリアライズとデシリアライズ]] * プロジェクトの作成 [#k5be1b87] #html(<div class="pl10">) 作業用フォルダ作成 #myterm2(){{ mkdir serialize_java cd serialize_java }} Javaプロジェクト作成 #myterm2(){{ gradle init --type java-application }} 不要なファイルを削除 #myterm2(){{ rm -rf src/test/java/* }} いちおう build.gradle の内容も貼っておく。 ※何も変更していない。(表示用にコメント削除しただけ) build.gradle #mycode2(){{ plugins { id 'java' id 'application' } mainClassName = 'App' dependencies { compile 'com.google.guava:guava:23.0' testCompile 'junit:junit:4.12' } repositories { jcenter() } }} #html(</div>) * まずは普通にシリアライズとデシリアライズ [#l8234bf4] #html(<div class="pl10">) まずは、普通にシリアライズとデシリアライズを実行する処理を作成していく。 最初にシリアライズ/デシリアライズを行う為の共通処理を作成。 ※以前に書いた シリアライズとデシリアライズ の内容とほぼ同じ。(違いは Base64エンコード/デコード にguavaを使用している事ぐらい) src/main/java/sample/MySerializer.java #mycode2(){{ 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 #mycode2(){{ 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 #mycode2(){{ 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(); } } } }} いちおう作成したファイルの状態を貼っておく。 #myterm2(){{ $ tree src src ├── main │   └── java │   ├── App.java │   └── sample │   ├── MyModule1.java │   └── MySerializer.java └── test └── java }} シリアライズ 及び デシリアライズを実行してみる。 #myterm2(){{ $ gradle run > Task :run serializedText: rO0ABXNyABBzYW1wbGUuTXlNb2R1bGUxAAAAAAAAAAECAAFMAAR2YXIxdAASTGphdmEvbGFuZy9TdHJpbmc7eHB0AANBQUE= var1: AAA BUILD SUCCESSFUL in 0s 2 actionable tasks: 2 executed }} まずは、正常に実行できる事を確認。 #html(</div>) * デシリアライズされるオブジェクトの定義がない場合 [#cbbeaf53] #html(<div class="pl20">) MyModule1 を削除(いったんリネーム) #myterm2(){{ mv src/main/java/sample/MyModule1.java src/main/java/sample/MyModule1.txt }} メイン処理を以下の通り変更。 src/main/java/App.java #mycode2(){{ /* コメントアウト 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 で削除してから実行する。 #myterm2(){{ 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でシリアライズとデシリアライズ]] ) #html(</div>) * 対象オブジェクトの定義内容が変更されている場合 [#w29cda2f] #html(<div class="pl20">) 次はオブジェクトの定義内容が変更されている場合の挙動を見ていく。 各ソースを以下の通り変更。 src/main/java/sample/MyModule1.java #mycode2(){{ 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 #mycode2(){{ 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ファイルをいったん削除して、再実行。 #myterm2(){{ $ 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でシリアライズとデシリアライズ]] と同じ結果。 #html(</div>)