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

# JSONIC とは

[JSONIC](https://github.com/hidekatsu-izuno/jsonic) (Simple JSON encoder/decoder written in java) はシンプルなで使いやすい Java 用 json ライブラリです。ライセンスは Apache 2.0。

既に古くなっており、公式でも [jackson](https://github.com/FasterXML/jackson) への移行を推奨しています。が、いまだに多くのシステムで使われているとおもいます。

# Java Object を JSON 化してみる

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

```java: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 テキストに変換してくれます。

```java: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/ab45a1ab-2e76-dbe7-4251-e167bf470109.png)
実行結果は以下になります。簡単ですよね。

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

# private inner class の扱い

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

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

```java: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 の表示を追加しただけ。

```java: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 内のロジックからは利用可能

```java: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 知識と経験の浅さに改めて気がついた、という記録でした。

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

それではまた！