WebSocket通信におけるJSONデータの利用方法 (Java , JavaScript)

# 背景
チャットアプリ作成を基準とし、サーバー⇔ブラウザ間でのWebSocket通信の記事を書いた。
サーバーをJava、クライアントをjavascriptで実装。

[ソケット通信に関する過去記事]
・[JavaとJavaScriptでwebブラウザとのソケット通信①](https://qiita.com/take4eng/items/d0b009c48ee8c3fe420a)
・[JavaとJavaScriptでwebブラウザとのソケット通信②](https://qiita.com/take4eng/items/ca6d25ac9bdd33d6bb67)

今回はチャットアプリを改造しつつ、『JSONデータの取扱方法』について学んでいく。
また、『ソケット通信でのJSONデータの送受信』についても学ぶ。

## 目的
1. Java、JavaScriptでのJSONの扱い方法を学ぶ。
2. Webソケット通信においてJSONでのデータの送受信方法を学ぶ。
3. JSONを用いてWebソケット通信の複数パスを実現する。

## 前提
この記事では主にJSONの扱い方を中心に記述していく。
ソケット通信についての解説は過去記事やググったページを参考にしてほしい。
⇒ サーバープログラムはJava、クライアントプログラムはJavaScriptで実装。

# JSONとは
http://www.tohoho-web.com/ex/json.html
例：`{ "name": "Tanaka", "age": 26 }`
連想配列のような形で値を保持しているもの。
要は**ただの文字列**と考えると楽に理解することができる。(本当は違うけど)
詳細は割愛。

# 実践内容
1. JavaScriptでJSONデータの取り扱い (エンコード、デコード)
2. JavaでJSONデータの取り扱い (エンコード、デコード)
3. ソケット通信におけるJSONデータの送受信方法
4. JSON送受信を利用した複数パスのチャットアプリ作成

# 1. JavaScriptでJSONデータの取り扱い
JavaScriptでは容易にJSONデータを扱うことができる。

## エンコード
オブジェクト ⇒ JSON
`JSON.stringify()`メソッドを使用する。

```JavaScript:使用例
var obj = {
    name: '太郎',
    age: 30,
    area: 'Tokyo'
}
var json = JSON.stringify(obj);
console.log(json);
```

```:実行結果
{"name":"Taro Tanaka","age":30}
```

1. 変数`obj`に連想配列の形式で値を代入。(オブジェクトの作成)
2. `JSON.stringify(obj)`を用いてエンコードし変数`json`に代入。(JSONへ変換)
3. `console.log(json)`でJSONデータをコンソールに表示。

実行結果を見ると、連想配列形式のオブジェクトがJSON形式に変換されていることが分かる。
⇒ キーや文字列は「" "」で囲われている。数値は裸のまま。

## デコード
JSON ⇒ オブジェクト
`JSON.parse()`メソッドを使用する。

```JavaScript:使用例
var obj1 = {
	    name: '太郎',
	    age: 30,
	    area: 'Tokyo'
	}
var json = JSON.stringify(obj1);
//-----ここまでJSONデータの準備-----
var obj2 = JSON.parse(json);
console.log(obj2);
console.log(obj2.name);
```

```:実行結果
{name: "太郎", age: 30, area: "Tokyo"}
太郎
```
1. 変数`obj1`に連想配列の形式で値を代入。(オブジェクトの作成)
2. `JSON.stringify(obj1)`を用いてエンコードし変数`json`に代入。(JSONへ変換)  
**----------ここまでJSONデータの準備 (前項のエンコードと同様)----------**
3. `JSON.parse(json)`を用いてデコードし変数`obj2`に代入。(オブジェクトへ変換)
4. `console.log(obj2)`でオブジェクトをコンソールに表示。
5. `obj2.～`で各プロパティにもアクセス可能。

実行結果を見ると、JSONデータがオブジェクトに変換されていることが分かる。
`obj1`(オブジェクト) ⇒ `json`(JSON) ⇒ `obj2`(オブジェクト)

# 2. JavaでJSONデータの取り扱い
Javaのオブジェクトはクラスを指し、JavaScriptのように簡単に作成できない。
JSONとオブジェクトを変換する場合、事前に**JSONの変数に対応したプロパティを持つクラス**を作成しておく必要がある。

JavaでJSONを扱うなら、外部ライブラリを使用することを推奨。
⇒ Java標準APIにもJSONを扱うものは用意されているが、かなり手間がかかる。

JSONを扱う外部ライブラリは以下のものが有名。

- Jackson
- GSON
- JSONIC
- JSON in Java　などなど

基本の利用方法は似たようなものだが、今回は「Jackson」を使用する。
他ライブラリの使用方法や外部ライブラリの適用方法は、参考ページを見るか適宜ググってほしい。

## エンコード
オブジェクト ⇒ JSON
エンコードには`ObjectMapper`クラスの`writeValueAsString()`メソッドを使用する。

```java:使用方法
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(オブジェクトインスタンス);
```

```java:使用例
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;

//JSONに変換するプロパティを持つクラス
class Info {
    public String name = "Taro Tanaka";
    public int age = 30;
}

//エンコードを実行するクラス
public class Main {
    public static void main(String[] args) {
        Info info = new Info();//JSONへ変換するクラスをインスタンス化
        ObjectMapper mapper = new ObjectMapper();//ObjectMapperクラスのインスタンスを作成
        try {
            //writeValueAsString()メソッドでエンコード実施
            String script = mapper.writeValueAsString(info);
            System.out.println(script);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

```:実行結果
{"name":"Taro Tanaka","age":30}
```

1. `Info`クラスにてJSONに変換するためのプロパティを作成する。
2. エンコードを実行する`Main`クラスを作成。
3. `Info info = new Info()`にてJSONに変換するオブジェクトをインスタンス化。
4. ` ObjectMapper mapper = new ObjectMapper()`にて`ObjectMapper`クラスをインスタンス化。
5. `mapper.writeValueAsString(info)`にてオブジェクトからJSONに変換。  
このとき、try,catchでのエラー対応が必要。
6. `System.out.println(script)`でJSONデータをコンソールに出力。

実行結果を見ると、オブジェクトクラスがJSON形式に変換されていることが分かる。
各プロパティの変数名と値がペアとなって格納されている。

## デコード
JSON ⇒ オブジェクト
デコードには`ObjectMapper`クラスの`readValue()`メソッドを使用する。

```java:使用方法
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(JSONデータ,オブジェクトクラス.class);
```

```java:使用例
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;

//JSONから変換されるプロパティを持つクラス
class Info {
    public String name;
    public int age;
}

//デコードを実行するクラス
public class Main {
    public static void main(String[] args) {
        String script = "{ \"name\":\"Taro Tanaka\", \"age\":30}";//文字列としてJSONデータを作成
        ObjectMapper mapper = new ObjectMapper();//ObjectMapperクラスのインスタンスを作成
        try {
            //readValue()メソッドでデコード実施
            Info info = mapper.readValue(script, Info.class);
            System.out.println(info.name);
            System.out.println(info.age);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

```:実行結果
Taro Tanaka
30
```

1. `Info`クラスにてJSONから変換されるプロパティを持つクラスを作成する。
2. デコードを実行する`Main`クラスを作成。
3. ここではStringの文字列としてJSONデータを用意する。
4. ` ObjectMapper mapper = new ObjectMapper()`にて`ObjectMapper`クラスをインスタンス化。
5. `mapper.readValue(script, Info.class)`にてJSONからオブジェクトに変換。  
このとき、try,catchでのエラー対応が必要。
6. `System.out.println(info.～)`でオブジェクトプロパティをコンソールに出力。

実行結果を見ると、JSONデータがオブジェクトクラスに変換されていることが分かる。
デコード後は指定したオブジェクトの各プロパティにアクセスすることで値を取得することができる。

# 3. ソケット通信におけるJSONデータの送受信方法
サーバープログラム：Java
クライアントプログラム：JavaScript

ソケット通信を行う際、JSONでの送受信方法を記述する。
JavaとJavaScriptで扱い方が異なるため、それぞれ解説する。

## JavaScriptでのJSON送受信

JavaScriptに関しては特筆すべきことはない。
前述した通り、`JSON.stringify()`メソッド及び`JSON.parse()`メソッドで簡単にオブジェクト ⇔ JSON間の変換が可能。
クライアントから送信前にエンコード、受信後にデコードすれば問題無い。

### 送信時エンコード
オブジェクト ⇒ JSON

```JavaScript:送信時
var obj = { type:'A' , msg:'a' };
var json = JSON.stringify(obj);
socket.send(json);
```

1. オブジェクトを用意。
2. `JSON.stringify()`メソッドでJSONへエンコード。
3. WebSocketの`send()`メソッドでJSONデータ送信

### 受信時デコード
JSON ⇒ オブジェクト

```JavaScript:受信時
socket.onmessage = function(e) {
    var obj = JSON.parse(e.data);
};
```

1. `onmessage`でJSONデータを受信。
2. `JSON.parse(e.data)`でオブジェクトへデコード

## JavaでのJSON送受信
Javaのソケット通信時にJSONを用いる場合、JavaScriptのように簡単にはいかない。
⇒ エンコーダー、デコーダー、オブジェクトクラスの3つを用意する必要がある。

### 送信時エンコード
オブジェクト ⇒ JSON

ソケット通信でJSONデータを送信する場合、エンコーダーを用いてオブジェクトからJSONに変換してから送信する。

通常、テキストデータを送信する場合には`sendText()`メソッドを使用するが、JSONを送信する場合は`sendObject()`メソッドを使用する。
引数をオブジェクトとし、エンコーダーによってJSONに変換したのち送信する。

1. 送信するオブジェクトを作成する。
2. エンコーダーを作成する。
3. @ServerEndpointアノテーションにエンコーダーを登録する。

#### 1. 送信するオブジェクトを作成
通常のクラスと同様にプロパティを持つクラスを作成する。
コンストラクタ、セッター、ゲッターも通常通り記述。(無くても変換可能。)

```Java:オブジェクトクラス
public class JsonObj {

	private String type = "type1";
	private String msg = "msg1";

	//コンストラクタ
	public JsonObj() {}

	//セッター
	public void setType(String type) {this.type = type;}
	public void setMsg(String msg) {this.msg = msg;}

	//ゲッター
	public String getType() {return type;}
	public String getMsg() {return msg;}
}
```

#### 2. エンコーダーを作成
エンコーダーは`javax.websocket`パッケージの`Encoder.Text`クラスを実装する。
(`Encoder.Binary`クラスも存在するが、バイナリデータを扱うためのクラスなので割愛)

`Encoder.Text<>`のジェネリクスにはエンコードするオブジェクトクラスを記述しておく。

```Java:エンコーダー
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonEncoder implements Encoder.Text<JsonObj>{

    @Override//初期化は何もしない
    public void init(EndpointConfig config) {}

	@Override//エンコード処理 ( オブジェクト → JSON )
	public String encode(JsonObj obj) throws EncodeException {
		ObjectMapper mapper = new ObjectMapper();
		String json = "";
		try {
			json = mapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return json;
	}

    @Override//破棄は何もしない
    public void destroy() {}
}
```

`Encoder.Text`クラスを継承した場合、以下のメソッドをオーバーライドする必要がある。

1. `init(EndpointConfig config)`：エンコーダーが起動したときの処理。
2. `encode(Object obj)`：エンコード処理。
3. `destroy()`：エンコーダが破棄された時の処理。

基本的に`init()`及び`destroy()`は何も処理しなくてよい。
`encode(Object)`にて**引数をオブジェクト**とし、エンコード後に**戻り値としてJSON**を指定。

#### 3. エンコーダーを登録

```Java:@ServerEndpoint
//エンコーダークラスを指定
@ServerEndpoint(value = "/json" , encoders = JsonEncoder.class)
public class JsonTest  {
    //中略

    //sendObject()の引数はオブジェクト
    //送信前にエンコードされる
    session.getAsyncRemote().sendObject(obj)

    //中略
}
```

エンコーダーを利用するとき、`@ServerEndpoint()`にエンコーダークラスを指定する。
ここで指定しておくことで、オブジェクト送信時に指定したエンコーダーによってJSONデータへ変換される。

- `sendObject(obj)` **⇒ エンコード処理 (obj→JSON) ⇒ 送信**

### 受信時デコード
JSON ⇒ オブジェクト

ソケット通信でJSONデータを受信する場合、デコーダーを用いてJSONからオブジェクトに変換してから受信する。

通常は`onMessage()`メソッドの引数にString型文字列として受信するが、デコーダーを用いることで受信前にデコード処理が実施され、オブジェクトとして受信することとなる。

1. 受信するJSONの変換先オブジェクトを作成する。
2. デコーダーを作成する。
3. @ServerEndpointアノテーションにデコーダーを登録する。

#### 1. 変換先のオブジェクトを作成
デコードする場合、JSONの要素に対応したプロパティを持つオブジェクトクラスを用意しておく必要がある。
複数のJSONを受信するとき内容や形式が異なるなら、それぞれのJSONに対応した複数のオブジェクトクラスを用意しなければならない。
>エンコードの場合、全てのオブジェクトは任意のタイミングでJSONに変換し送信することが可能
デコードの場合、受信するJSONの内容をあらかじめ把握し、デコード前に受け口となるオブジェクトクラスを用意しておかなければならない。

```Java:オブジェクトクラス
public class JsonObj {

	private String type;
	private String msg;

	//コンストラクタ
	public JsonObj() {}

	//セッター
	public void setType(String type) {this.type = type;}
	public void setMsg(String msg) {this.msg = msg;}

	//ゲッター
	public String getType() {return type;}
	public String getMsg() {return msg;}
}
```

#### 2. デコーダーを作成
デコーダーは`javax.websocket`パッケージの`Decoder.Text`クラスを実装する。
(`Decoder.Binary`クラスも存在するが、バイナリデータを扱うためのクラスなので割愛)

`Decoder.Text<>`のジェネリクスにはデコード先のオブジェクトクラスを記述しておく。

```Java:デコーダー
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonDecoder  implements Decoder.Text<JsonObj> {

	@Override//初期化は何もしない
	public void init(EndpointConfig config) {}

	@Override//デコードできるかの判定
	public boolean willDecode(String text) {
		return (text != null);
	}

	@Override//デコード処理 ( JSON → オブジェクト)
	public JsonObj decode(String text) throws DecodeException {
		ObjectMapper mapper = new ObjectMapper();
		JsonObj obj = null;
		try {
			obj = mapper.readValue(text, JsonObj.class);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return obj;
	}

	@Override//破棄は何もしない
	public void destroy() {}
}
```

`Decoder.Text`クラスを継承した場合、以下のメソッドをオーバーライドする必要がある。

1. `init(EndpointConfig config)`：デコーダーが起動したときの処理。
2. `willDecode(String text)`：デコード処理を実行するかどうかの判定。
3. `decode(Object obj)`：デコード処理。
4. `destroy()`：デコーダが破棄された時の処理。

基本的に`init()`及び`destroy()`は何も処理しなくてよい。
`willDecode()`の**引数をJSON**とし、戻り値が`true`なら以下のデコードを実行。`false`ならデコードされず以降の`@OnMessage`メソッドは実行されない。
`decode()`にて**引数をJSON**とし、エンコード後に**戻り値としてオブジェクト**を指定。

#### 3. デコーダーを登録

```Java:@ServerEndpoint
//デコーダークラスを指定
@ServerEndpoint(value = "/json" , decoders = JsonDecoder.class)
public class JsonTest  {
    //中略

    //@OnMessageメソッドの前にデコードされる
    @OnMessage
    public void onMessage(JsonObj obj , Session mySession) {
    }

    //中略
}
```

デコーダーを利用するとき、`@ServerEndpoint()`にデコーダークラスを指定する。
ここで指定しておくことで、データ受信時に指定したデコーダーによってオブジェクトへ変換される。
⇒ `@OnMessage`メソッドの引数は**オブジェクト型**となる。(通常はString型)

- **受信 ⇒ デコード処理 (JSON→obj) ⇒** `@OnMessage`**メソッド**

# 4. 複数パスのチャットアプリ作成
[過去](https://qiita.com/take4eng/items/ca6d25ac9bdd33d6bb67)に作成したチャットアプリを改変する。

## 変更点
1. 送受信するデータをテキストからJSONへ変更 (JSONの使用)
2. チャット欄を1つから2つへ増加 (複数パスの実現)

>WebSocket通信ではデータを受信するための受け口が1つしかない。
つまりワンパスのため複数の送信元があっても見分けがつかない。
もし見分けるなら、文字列の内容を分解して区別する他無いだろう。
どうせデコードするのならJSONを扱うのが都合がよい。
>>実際は 1.テキスト 2.バイナリ 3.PingPong と3種類の受信メソッドがあるが、ここではテキスト形式のみを扱うため受け口は1つと考える。

## 作成ファイル

1. JsonIndex.html：ブラウザ表示用のHTMLファイル
2. JsonSocket.js：ソケット通信のクライアントプログラム
3. JsonTest.java：ソケット通信のサーバープログラム
4. JsonObj.java：JSONと相互変換するオブジェクトクラス
5. JsonEncoder.java：Javaエンコーダー
6. JsonDecoder.java：Javaデコーダー

### 1. 表示用HTML
```HTML:JsonIndex.html
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>JSON送受信</title>
	<script type="text/javascript" src="JsonSocket.js"></script>
</head>
<body>
	<div
		style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;"
		id="show1"></div>
	<input type="text" size="80" id="msg1" name="msg1" />
	<input type="button" value="送信" onclick="sendMsg1();" />
	<p></p>
	<div
		style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;"
		id="show2"></div>
	<input type="text" size="80" id="msg2" name="msg2" />
	<input type="button" value="送信" onclick="sendMsg2();" />
</body>
</html>
```

**ポイント**
1. チャット欄を2つに増加した。  
表示枠はそれぞれ`show1`と`show2`、テキストボックスは`msg1`と`msg2`とした。  
後述するJavaScriptファイルでこれらを操作する。

### 2. クライアントプログラム
```javascript:JsonSocket.js
//JSON用のオブジェクト作成
var obj = { type:null , msg:null };

//WebSocketオブジェクト生成
var wSck= new WebSocket("ws://localhost:8080/jsonTest/json");

//ソケット接続時のアクション
wSck.onopen = function() {
	document.getElementById('show1').innerHTML += "接続しました。" + "<br/>";
	document.getElementById('show2').innerHTML += "接続したよ～" + "<br/>";
};

//メッセージを受け取ったときのアクション
wSck.onmessage = function(e) {
	//JSONデータをオブジェクトへデコード
	var json = JSON.parse(e.data);

	//JSONデータのtype値によって実行内容を変更
	if(json.type === 'msg1'){document.getElementById('show1').innerHTML += json.msg + "<br/>";}
	else if(json.type === 'msg2'){document.getElementById('show2').innerHTML += json.msg + "<br/>";}
};

//メッセージ送信1
var sendMsg1 = function(val) {
	var element = document.getElementById('msg1')
	obj.type = element.name;//オブジェクトの内容を代入
	obj.msg = element.value;
	var json = JSON.stringify(obj);//オブジェクトをJSONへエンコード
	wSck.send(json);//JSONを送信
	element.value = "";//内容をクリア
};

//メッセージ送信2
var sendMsg2 = function(val) {
	var element = document.getElementById('msg2');
	obj.type = element.name;
	obj.msg = element.value;
	var json = JSON.stringify(obj);
	wSck.send(json);
	element.value = "";
};
```

**ポイント**
1. JSON用のオブジェクト`obj`を作成 ⇒ これがJSONへ変換される。
2. ソケット接続時のアクションをチャット欄増加に合わせて追加。
3. メッセージ受信時にJSONをオブジェクトへデコードし、type値によって処理内容を変更。
4. メッセージ送信1,2：`obj`に値を代入後、サーバーへの送信前にJSONへの変換を実施。

### 3. サーバープログラム
```Java:JsonTest.java
package jsonTest;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

// 引数にデコーダー、エンコーダーを記述
@ServerEndpoint(value = "/json" , decoders = JsonDecoder.class , encoders = JsonEncoder.class)
public class JsonTest  {

	private static Set<Session> user = new CopyOnWriteArraySet<>();

	@OnOpen
    public void onOpen(Session mySession) {
        System.out.println("connect ID:"+mySession.getId());
        user.add(mySession);
    }

	//このメソッドの前にデコードされる
    @OnMessage
    public void onMessage(JsonObj obj , Session mySession) {
    	for (Session user : user) {
    		user.getAsyncRemote().sendObject(obj);//送信するものはオブジェクト(送信前にエンコードされる)
            System.out.println(user.getId()+"番目に"+mySession.getId()+"番目のメッセージを送りました！");
    	}
    	if(obj.getMsg().equals("bye")) {onClose(mySession);}
    }

    @OnClose
    public void onClose(Session mySession) {
    	System.out.println("disconnect ID:"+mySession.getId());
        user.remove(mySession);
        try {
			mySession.close();
		} catch (IOException e) {
			System.err.println("エラーが発生しました: " + e);
		}
    }
}
```

**ポイント**
1. `@ServerEndpoint`にデコーダー及びエンコーダーのクラスを指定。
2. クライアントからデータを受信したとき、`@OnMesaage`メソッドが実行される前に指定したデコーダーが実行されJSONからオブジェクトに変換される。  
⇒ `@OnMesaage`メソッドの引数は`Obj`型となる。
3. `sendObject(obj)`メソッドでオブジェクトを送信するとき、クライアントへ送信する前に指定したエンコーダーが実行されオブジェクトからJSONに変換される。  
※ 文字列を送信する場合は`sendText()`を使用するが、オブジェクトを送信する場合は`sendObject()`を使用する。

### 4. オブジェクトクラス

```Java:JsonObj.java
package jsonTest;

public class JsonObj {

	private String type;
	private String msg;

	//コンストラクタ
	public JsonObj() {}

	//セッター
	public void setType(String type) {this.type = type;}
	public void setMsg(String msg) {this.msg = msg;}

	//ゲッター
	public String getType() {return type;}
	public String getMsg() {return msg;}
}
```

**ポイント**
1. このクラスのプロパティは`type`と`msg`の2つ。  
⇒ クライアントプログラムのオブジェクト (受信するJSON) に対応したクラスを作成している。
2. デコード後、`type`プロパティ値によって送信元を判断する。

### 5. エンコーダー

```Java:JsonEncoder.java
package jsonTest;

import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonEncoder implements Encoder.Text<JsonObj>{

    @Override//初期化は何もしない
    public void init(EndpointConfig config) {}

	@Override//エンコード処理 ( オブジェクト → JSON )
	public String encode(JsonObj obj) throws EncodeException {
		ObjectMapper mapper = new ObjectMapper();
		String json = "";
		try {
			json = mapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return json;
	}

    @Override//破棄は何もしない
    public void destroy() {}
}

```

**ポイント**
1. `Encoder.Text<JsonObj>`を実装したクラスを作成。
2. `encode()`メソッドの引数はオブジェクト、戻り値はJSONとなる。
3. エンコードには`ObjectMapper`クラスの`writeValueAsString()`メソッドを使用する。

### 6. デコーダー

```Java
package jsonTest;

import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonDecoder  implements Decoder.Text<JsonObj> {

	@Override//初期化は何もしない
	public void init(EndpointConfig config) {}

	@Override//デコードできるかの判定
	public boolean willDecode(String text) {
		return (text != null);
	}

	@Override//デコード処理 ( JSON → オブジェクト)
	public JsonObj decode(String text) throws DecodeException {
		ObjectMapper mapper = new ObjectMapper();
		JsonObj obj = null;
		try {
			obj = mapper.readValue(text, JsonObj.class);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return obj;
	}

	@Override//破棄は何もしない
	public void destroy() {}
}
```

**ポイント**
1. `Decoder.Text<JsonObj>`を実装したクラスを作成。
2. `decode()`メソッドの引数は文字列(JSON)、戻り値はオブジェクトとなる。
3. エンコードには`ObjectMapper`クラスの`readValue()`メソッドを使用する。

## 実行結果
Chromeにて`JsonIndex.html`を実行。
![JSONチャット.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634805/beddcfc2-c246-2a53-3808-32992df2b214.gif)

チャット欄が2つに増加した。
各チャット欄で独立して送受信が可能。
⇒ 複数パスの実現が完了。

### 実行順序
長くなったので、クライアントとサーバーでどのような処理が行われているか記述する。

1. HTMLでページを表示。
2. ハンドシェイク完了後、ソケット通信開始。
3. テキストボックスにメッセージを記述し送信ボタンをクリック。
4. JavaScriptによりオブジェクトをJSONにエンコードしサーバーへ送信。
5. サーバーでJSONデータを受信。
6. デコーダーによりJSONからオブジェクトへデコード。
7. デコードされたオブジェクトを引数に`@OnMessage`メソッドを実行。
8. `sendObject()`メソッドでオブジェクトを送信。
9. 送信前にエンコーダーによりオブジェクトからJSONへエンコードし、クライアントへ送信。
10. クライアントでデータ受信。
11. JSONデータをオブジェクトへデコード。
12. `type`プロパティ値によって送信元を判別し、各送信元へメッセージを表示。

# その他
## 改善点
・未知のJSONデータを受信すると、対応したオブジェクトクラスを用意していないためエラーとなる。
⇒ 対応している人はいるので、そのうち調べるかも。結構ややこしそう。

・複数の型のJSONデータを受信する場合に未対応。
⇒ そこまで難しくなさそう。デコーダーとオブジェクトクラスを増やしてJSONの中身に対応すればできる。

・入れ子構造のJSONに対応するにはどういうオブジェクトクラスを作成すればいいか不明。
⇒ ググれば出るし、必要となれば調べる。

・バイナリデータの扱いは理解が進んでいないため、バイナリデータの送受信には未対応。
⇒ 画像の送受信など、必要となれば調べる。

## 感想
思った以上にややこしかった。長くなってしまったので反省。
もっとまとめるべきだった。

JavaでJSONを扱うのに、外部ライブラリを使用したりオブジェクトクラスを用意したりと面倒。
JavaScriptなら1行で終わるのにね。

Javaのソケット通信でJSONを扱うときに、エンコードクラスやデコードクラスが用意されているのは便利なのかどうなのか。
わざわざエンコーダークラスやデコーダークラスを用意しなくちゃいけないのは、面倒といえば面倒。

面倒だが、JSONを使いたいならしょうがない。
JavaでのJSONの扱い方について理解が深まって良かった。
個人制作レベルなら利用していけそう。

# 参考ページ
- [JavaScriptでJSONデータのエンコード、デコード](https://www.sejuku.net/blog/47716)
- [JavaでJSONデータのエンコード、デコード (Jackson)](https://style.potepan.com/articles/17019.html)
- [JavaでJSONデータのエンコード、デコード (Jackson特化)](https://qiita.com/opengl-8080/items/b613b9b3bc5d796c840c)
- [JavaでJSONデータのエンコード、デコード (Jackson 他)](https://www.sejuku.net/blog/39599)
- [ソケット通信におけるJSONの送受信](https://www.codeflow.site/ja/article/java-websockets)
- [ソケット通信におけるJSONの送受信 (マルチオブジェクト)](http://enterprisegeeks.hatenablog.com/entry/2016/01/11/072923)