Spring Boot / Controller / レスポンス / JSON / Jackson に頼る

Spring Boot / Controller / レスポンス / JSON / Jackson に頼る

Spring Boot には Java の JSON オペレーションライブラリの Jackson が内蔵されている。 これを使うと Java のインスタンスを楽に JSON 化できる。

基本

このようにコントローラークラス自体を @RestController アノテーション指定にして、メソッドがそのように作られた(アクセサがある)インスタンスを返すように記述すると、それが自動的に JSON 化されてレスポンスされる。その名前は getter から自動的に類推される。

@RestController
@RequestMapping("/json/hoge")
public class HogeJsonController {
    @RequestMapping("")
    public Hoge index() {
        Hoge hoge = new Hoge();
        hoge.setV1("hogehogehoge");
        hoge.setV2(123);
        return hoge;
    }
    public static class Hoge{
        private String v1;
        private Integer v2;
 
        public String getP1(){
            return this.v1;
        }
        public void setV1(String v){
            this.v1 = v;
        }
        public Integer getP2(){
            return this.v2;
        }
        public void setV2(Integer v){
            this.v2 = v;
        }
 
    }
}

このように記述しておくと、↓のようなレスポンスで返される

{"p1":"hogehogehoge","p2":123}

@Controller アノテーション指定されている Conbtroller クラス中で一部のメソッドだけ JSON を返したいという場合はこのように @ResponseBody アノテーションを戻す型指定にくっつける。

@RequestMapping("/hogehoge")
public @ResponseBody HogeHoge hogehoge() {

Getterを省略する

public にすることで getter を作らずにそのメンバ名のまま出力できる。

public static class Hoge{
    public String v1;
    public Integer v2;        
}

このように記述しておくと、↓のようなレスポンスで返される

{"v1":"hogehogehoge","v2":123}

便利なようだが、このようなインスタンスは必ず別の構造を持つインスタンス由来になっているので getter がなくなるという状況はよほど単純か手抜き仕様でなければないと思われる。 こいつは View なので元になるクラスがこいつに依存することもないし。

キーを任意の順番で出す

このように出力するクラスに @JsonPropertyOrder をつけて出したいキーの順番に書けば順番に出る。

@JsonPropertyOrder({"v2", "v1"})
public static class Hoge{
    public String v1;
    public Integer v2;        
}

JSON変換用クラスを用意する

データベースに入れた情報をなんでもかんでもクライアント側に返してよいわけが無い。 Jackson では何も考えず単に return するとそれが全部出力されてしまう。 なので自分の知らないところで仕様が変更されて(秘密な情報が追加等)もそれに気づかなければそのまま出てしまう。

なので出力する情報は選ぶようにして実装する必要がある。

HTML だったらちゃんと選んで成形して出すのに JSON になったとたん DB の中身全部出せばいいとか言い出す人が多くてビビる。

JSON出力用メソッドを作って素手で細かく構築する

JSON化対象のクラスのJSON化に使いたいメソッドに @JsonValue アノテーションをつける。そうするとそこで構築した Map で JSON を生成してくれる。 構造が込み入っている場合はこれを利用してもよいかも。

逆にこれさえ知っておけばどんな形式のデータからでも任意の JSON を作れるのでプロジェクトのルールとして変換は全部これで書くみたいなことになってもいいかも

public class Hoge {
    // 中略
    @JsonValue
    public Map<String, Object> toMapForJson() {
        Map<String, Object> map = new HashMap<>();
        map.put("v_v_v_1", 123);
        map.put("v_v_v_v_2", "hogehoge");
        return map;
    }
}

JSON を JSON として返す

もうシステムの中でも JSON的な文字列としてDB等に保存していて、それをそのまま出したい場合どうするか。

そのままやってしまうと、ダブルクォートで囲まれたそのままの JSONとしても文字列でJSONっぽい文字列として出力されてしまう。JSON中に Object として出力したい場合はどうするか?。

この場合は一旦別のクラスでこの文字列をラップしてやればよい。 Doma を使っているならドメインクラスがあるのでこれを利用すればよい。

class JsonString{
    public String value;
 
    public JsonString(String v){
        this.value = v;
    }
 
    @JsonRawValue
    @JsonValue
    public String asRawJsonForJackson(){
        return this.value;
    }
}

JsonValue のアノテーションと JsonRawValue のアノテーションを2つつける。

そしてこのラッパーで包んだ状態で map にブチ込んでやると、文字列として解釈されず内蔵した JSON がそのまま JSON として出力される。

@Entity
public class Hoge {
    // 中略
    @JsonValue
    public Map<String, Object> toMapForJson() {
        Map<String, Object> map = new HashMap<>();
        map.put("id", this.id.getValue());
        map.put("piyo", new JsonString(this.piyo));
        return map;
    }
}
java/spring/spring_boot/controller/response/output_json/use_jackson/start.txt · 最終更新: 2021-06-25 18:11 by ore