JacksonのObjectMapperがかなり優秀だった話

はじめに

はじめまして。エキサイトでインターンをさせていただいている岡﨑です。 アドベントカレンダーの22日目を担当させていただいています。 今日はJacksonのObjectMapperがとてもとても便利だった話をさせていただきます。

Object Mapperとは

それでははじめに言葉の定義からです。 Object Mapperとはそもそも何なのか、この機会に調べてみました。 まずは公式ドキュメントからです。

ObjectMapperは、基本的なPOJO(Plain Old Java Objects)との間、または汎用JSONツリーモデル(JsonNode)との間でJSONを読み書きするための機能と、変換を実行するための関連機能を提供します。また、さまざまなスタイルのJSONコンテンツを処理し、ポリモーフィズムやオブジェクトIDなどのより高度なオブジェクトの概念をサポートするように高度にカスタマイズできます。 公式ドキュメントより

簡単に噛み砕くと、 JacksonのObjectMapperはJavaのObjectとJson間の変換を簡単にしてくれるクラスです。

どれくらい簡単になるの?

それでは実際にやってみましょう。 JSONだとこんな感じです。

{
    "sample_id": 1,
    "sub_list1": [
        {
            "sub_id": 11,
            "sub2_list": [
                {
                    "sub2_id": 111,
                    "answer": "test"
                },
                {
                    "sub2_id": 121
                }
            ]
        },
        {
            "sub_id": 12,
            "sub2_list": [
                {
                    "sub2_id": 211,
                    "answer": "test2"
                },
                {
                    "sub2_id": 212,
                    "answer": "test3"
                }
            ]
        }
    ]
}

これを受け取り、モデルに変換していきたいと思います。

@Slf4j
@Value
public class SampleForm {
    @NotNull
    Sample samle;

     @Value
    public static class Sub2 {
        @NotNull
        Integer subId2;

        @NotNull
        String answer;

        @JsonCreator
        public Sub2 (
            JsonProperty("sub_id2") Integer subId2; 
            @JsonProperty("answer") String answer; 
        ) {
            this.subId2 = subId2;
            this.answer = Optional.ofNullable(answer).orElse("");
        }
    }

    @Value
    public static class Sub1 {
        @NotNull
        Integer subId;

        @NotNull
        List<Sub2> sub2List;

        @JsonCreator
        public Sub1 (
            JsonProperty("sub_id") Integer subId; 
            @JsonProperty("sub2_list") List<Sub2> sub2List; 
        ) {
            this.subId = subId;
            this.sub2List = sub2List;
        }
    }

    @Value
    public static class Sample {
        @NotNull
        Integer sampleId;

        @NotNull
        List<Sub1> subList1;

        @JsonCreator
        public Sample (
            @JsonProperty("sample_id") Integer sampleId,
            @JsonProperty("sub_list1") List<Sub1> subList1
        ) {
            this.sampleId = sampleId; 
            this.subList1 = subList1;
        }
    }

    @ConstructorProperties({
        "sample"
    })
    public SampleForm(
        Sample sample
    ) {
        this.sample = sample;
    }
}

こんな感じでできたらいいなと思いますよね。 しかし、これでは動きません。JSONのデータからそのままモデルに変換することはできないから当然です。 この場合はSampleがnullになって落ちます。

それでは、次のコードを書き換えていきましょう。

    @ConstructorProperties({
        "sample"
    })
    public SampleForm(
        Sample sample
    ) {
        this.sample = sample;
    }

ここがモデルで受け取ることはできないので、Sampleクラスで受け取っているところをStringで受けとってあげます。 そして、Sampleクラスに変換します。

さて、本題です。 ここで長々とコードを書かずに、数行で全て変換してくれるObjectMapperの登場です。

    @ConstructorProperties({
        "sample"
    })
    public SampleForm(
        String sample
    ) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            this.sample = mapper.readValue(sample, new TypeReference<>() {});
        } catch(Excepting exceptin) {
            new Exception("モデルに変換できませんでした");
        }
    }

ObjectMapper mapper = new ObjectMapper();

this.sample = mapper.readValue(sample, new TypeReference<>() {});

この二行を使うだけで、意図している通りにモデルを変換することができます。 とても便利ですね。

終わりに

Object Mapperがここまでできることを知らないで実装すると、とてもごちゃごちゃとしたやばいコードを書くことになります。 それを防ぐためにも、こうして使えるものは正しく使っていくことが大切だと思いました。

アドベントカレンダーも終盤ですが、最後まで楽しんでいただけると幸いです。

qiita.com