こんにちは。夏が大好きなtomita@atuwebです。

先日Java + Spring Fraeworkで、サーバ/クライアント間の通信についてまとめました。

[Java][Spring]クライアントとJSONで通信するためのバックエンド実装

上記ではさらっとしか触れなかった、特殊文字をunicodeシーケンスにエスケープする実装について整理いたしました。

Overview

何をするのか

jacksonを利用したJson、Objectのコンバートで特殊文字をエスケープする。

Cocos2d-xで実装したクライアントとの通信で、クライアント側C++のJsonパーサーでは特殊文字の扱いが苦手であったため、特殊文字をエスケープすることで回避したのでした。

開発環境

  • Java 7
  • Spring 4
  • Maven 3
  • jackson 2.4

数年前の実装のためJava7です。
新しいプロジェクトでは最新のバージョンをご利用ください。

エスケープ挙動

この記事でJsonにコンバートするオブジェクトの構造です。

@Data
public class ResponsetBean implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer missionId;
    private String  missionName;

    private Integer difficulty;
}

上記POJOのインスタンスを生成し、値を入れておきます。

ResponsetBean response = new ResponsetBean();
response.setMissonId(1188);
response.setMissionName("時のはざま");
response.setDifficulty(8);

エスケープなし

NewしたObjectMapperをそのまま利用する、通常の挙動です。

  • 実行コード
try {
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(response);
} catch (Exception e) {
    throw new RuntimeException(e);
}
  • 得られるJson文字列
{"missionId":1188,"missionName":"時のはざま","difficulty":8}

マルチバイト文字をエスケープ

マルチバイト文字をエスケープしましょう。
マッパー設定にFeature.ESCAPE_NON_ASCII、trueを設定します。

  • 実行コード
try {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(Feature.ESCAPE_NON_ASCII, true);
    String json = mapper.writeValueAsString(response);
} catch (Exception e) {
    throw new RuntimeException(e);
}
  • 得られるJson文字列
{"missionId":1188,"missionName":"\u6B21\u5143\u306E\u306F\u3056\u307E","difficulty":8}

マルチバイト文字がエスケープされunicodeシーケンス化されていることが分かります。

さらなる敵

ここでさらなる敵が、、、
なんと裏ボスが覚醒しました。

ミッション名「次元のはざま」を「次元のはざま/”覚醒”」に変化します。

インスタンスに与える文字を変更し。

response.setMissionName("次元のはざま/\"覚醒\"");

上のパーサに与えると、次のJson文字列が得られます。

{"missionId":1188,"missionName":"\u6B21\u5143\u306E\u306F\u3056\u307E/\"\u899A\u9192\"","difficulty":8}

なかなか正規表現が難しそうな形になりましたね。

そこで、エスケープを拡張します。

特殊文字をエスケープ

ObjectMapperに与える、特殊エスケープのマッピング を定義したエスケープ拡張クラスを作成します。
次にコードを示します。

エスケープ拡張クラス

import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.io.CharacterEscapes;

@SuppressWarnings("serial")
public class CustomCharacterEscapes extends CharacterEscapes {

    private final int[] asciiEscapes;

    public CustomCharacterEscapes()
    {
        int[] esc = CharacterEscapes.standardAsciiEscapesForJSON();
        esc['"']  = CharacterEscapes.ESCAPE_STANDARD;
        esc['\''] = CharacterEscapes.ESCAPE_STANDARD;
        esc['/']  = CharacterEscapes.ESCAPE_STANDARD;
        esc['\n'] = CharacterEscapes.ESCAPE_STANDARD;
        asciiEscapes = esc;
    }

    @Override
    public int[] getEscapeCodesForAscii() {
        return asciiEscapes;
    }

    // no further escaping (beyond ASCII chars) needed:
    @Override
    public SerializableString getEscapeSequence(int ch) {
        return null;
    }
}

マルチバイト文字、および次の文字をunicodeシーケンスにコンバートします。

  • ” (ダブルクォーテーション)
  • ‘ (シングルクォーテーション)
  • / (スラッシュ)
  • \n (改行コード)

エスケープしたい特殊文字を追加する場合、変数escに追加していってください。

実行コードと結果

  • 実行コード
try {
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(Feature.ESCAPE_NON_ASCII, true);
    mapper.getJsonFactory().setCharacterEscapes(new CustomCharacterEscapes());
    String json = mapper.writeValueAsString(response);
} catch (Exception e) {
    throw new RuntimeException(e);
}
  • 得られるJson文字列
{"missionId":1188,"missionName":"\u6B21\u5143\u306E\u306F\u3056\u307E\u002F\u0022\u899A\u9192\u0022","difficulty":8}

ダブルクォーテーション、スラッシュともにunicodeシーケンスに変換されていることが確認できました。

getJsonFactory@Deprecatedなのはゴメンナサイ。
代替コードを調べ中です。

おわりに

今回のサンプルはオブジェクトからJsonへのコンバートのみでしたが、Jsonからオブジェクトへバインディングする際も同様のコードで実装できます。

楽しい開発ライフを過ごしてください。


スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
コメントの入力は終了しました。