LoginSignup
0
0

More than 1 year has passed since last update.

JSONIC で POJO の JSON 変換を試してみたら private class まで出力してくれた件

Posted at

JSONIC とは

JSONIC (Simple JSON encoder/decoder written in java) はシンプルなで使いやすい Java 用 json ライブラリです。ライセンスは Apache 2.0。

既に古くなっており、公式でも jackson への移行を推奨しています。が、いまだに多くのシステムで使われているとおもいます。

Java Object を JSON 化してみる

とりあえず、以下のような簡単な古典的な Java Object (POJO: Plain Old Java Object) を用意してみます。

User.java
public class User {
	public int age;
	private String name;
	public String getName() {
		return this.name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

以下のようにインスタンスを生成して JSON.encode() 関数を呼び出すと、インスタンスを json テキストに変換してくれます。

TestCode.java
import net.arnx.jsonic.JSON;

public class TestCode {
	public static void main(String[] args) {
		User user = new User();
		user.setName("yamachan");
		user.age = 17;
		
		String json = JSON.encode(user);
		System.out.println(json);
	}
}

Eclipse 上はこんな感じ。
image.png
実行結果は以下になります。簡単ですよね。

{"age":17,"name":"yamachan"}

private inner class の扱い

某所で JSONIC を用いた Java アプリのコードを眺めたのですが、private inner class を用いたものがありました。面白いなと思って、自分でもちょっと試してみます。

さきほどの User クラスの中に、InnerUser という名前で、もうひとつの POJO クラスを private inner class として定義してみます。以下のような感じ。

User.java
public class User {
	public User() {
		this.innerUser = new InnerUser();
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
		this.innerUser.setName("#" + this.name);
	}
	public Object getInnerUser() {
		return this.innerUser;
	}
	public int age;
	private String name;
	private InnerUser innerUser;
	
	private class InnerUser {
		public int age;
		private String name;
		public String getName() {
			return this.name;
		}
		public void setName(String name) {
			this.name = name;
		}
	}
}

実行コードはほとんど同じ。最後に innerClass の表示を追加しただけ。

TestCode.java
import net.arnx.jsonic.JSON;

public class TestCode {
	public static void main(String[] args) {
		User user = new User();
		user.setName("yamachan");
		user.age = 17;
		
		String json = JSON.encode(user);
		System.out.println(json);
		System.out.println(user.getInnerUser()); // コレを追加
	}

}

実行結果は以下のようになりました。

private class として定義した InnerUser クラスを Object 型として返しただけなのに、ちゃんと中身が JSON 化されて表示されています。これは便利!でも、どうして?

{"age":17,"innerUser":{"age":0,"name":"#yamachan"},"name":"yamachan"}
jsonic.User$InnerUser@7a92922

この動作ですが、私としては以下のように理解しました。

  • InnerClass は private class なので User class 以外からは利用できない
  • しかし User class に public Object getInnerUser() 関数が定義されているので、Object 型の参照として外部からアクセスすることはできる
  • 上記の得られた Object を String に変換すると User$InnerUser@7a92922 と inner class 名とシグネチャがわかる、つまりこれらの情報は守られていない
  • InnerClass に定義された age 属性値や、getName() メソッドが public なので、これらの値にも外部からアクセスでき、結果として JSONIC により JSON テキスト化の対象となる

そして、InnerClass に格納した値を守るためには、以下の対応が必要のようです。

  • InnerUser class 定義で age setName() getName() などの属性やメソッドを private に変更する
  • この private への変更後も定義した User class 内のロジックからは利用可能
User.java の一部
	private class InnerUser {
		private int age;
		private String name;
		private String getName() {
			return this.name;
		}
		private void setName(String name) {
			this.name = name;
		}
	}

うん、はい。。なんか Java として当然なことを言っているだけの気がしてきました…

私はこれまで、あまり Java の private inner class を使ってこなかったので、見慣れないコードなだけでした。既存のクラスを private inner class として取り込む際には、public な setter/getter なども忘れずに private に変更すること。

そうじゃないとオブジェクトへの参照が外に漏れた途端、inner class そのものが守られているように見えても、JSONIC みたいなリフレクションを使った(?)コードから、中の値を見られてしまうよ、ということですね。気を付けよう。。

というわけで

JSONIC の動作が気になって試してみたら、自分自身の Java 知識と経験の浅さに改めて気がついた、という記録でした。

うーん、単に自己反省メモで、情報量がほとんどないですね。すみません。。

それではまた!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0