Jackson の痒いところ Tips

  • 102
    いいね
  • 0
    コメント

Jackson の、普段あんまり使わない機能過ぎて使いたいときに忘れてぐぐって時間を浪費してしまうようなケースを回避するためにメモメモします。随時更新予定。

見ればわかりますが、元ネタはほぼすべて Stack Overflow です。英語だけだと読むのが辛い、という方向け(主に自分)にまとめています。

snake_case でプロパティを表現している JSON と camelCase で表記している POJO をマッピングしたい

java - Jackson overcoming underscores in favor of camel-case - Stack Overflow より。

引数に PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES PropertyNamingStrategy.SNAKE_CASE を指定して、 ObjectMapper#setPropertyNamingStrategy() を呼び出すと、このマッピングをする ObjectMapper を得ることができます。

(2016-09-29 更新: PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES は deprecated になりました。代わりに PropertyNamingStrategy.SNAKE_CASE を使う必要があります)

以下、利用例です。

    public static class Hoge {
        // このフィールドは JSON の "hoge_fuga" プロパティに対応する
        public String hogeFuga;
    }

    public static void snakeCaseJsonToCamelCasePojo() throws IOException {
        ObjectMapper mapper = new ObjectMapper()
                .setPropertyNamingStrategy(
                        PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);

        Hoge hogeObj = mapper.readValue("{\"hoge_fuga\": \"piyopiyo\"}", Hoge.class);
        System.out.println(hogeObj.hogeFuga);
    }

ObjectMapper#writeValue() などで出力される JSON を整形&インデント (pretty print) したい

書式は何でもいいので、とにかくお手軽に整形したい

java - Pretty printing JSON from Jackson 2.2's ObjectMapper - Stack Overflow より。

引数に SerializationFeature.INDENT_OUTPUT を指定して ObjectMapper#enable() を呼び出すと、pretty print を有効にした ObjectMapper を得ることができます。

以下、利用例です。

    public static class Fuga {
        public List<String> strings = Arrays.asList("foo", "bar", "baz");
        public int number = 3;
    }

    public static void withPrettyPrint() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper()
                .enable(SerializationFeature.INDENT_OUTPUT);

        String json = mapper.writeValueAsString(new Fuga());

        System.out.println(json);
    }

出力結果はこんな感じ。この出力結果はそうでもないですが、標準の pretty printer はクセがあるので要注意です。

{
  "strings" : [ "foo", "bar", "baz" ],
  "number" : 3
}

書式はそのままでいいけど、インデント数は 2 つから 4 つに変更したい

java - Custom pretty printer using Jackson library - Stack Overflow より。

DefaultPrettyPrinter が利用している Indenter を標準の DefaultPrettyPrinter.Lf2SpacesIndenter から独自のものに差し替えてあげることで実現できます。

    /**
     * インデント 4 つの Indenter 実装です。
     */
    public class Lf4SpacesIndenter extends DefaultPrettyPrinter.Lf2SpacesIndenter {
        final static char[] LONG_SPACES = new char[64 * 2];
        static {
            Arrays.fill(LONG_SPACES, ' ');
        }

        @Override
        public void writeIndentation(JsonGenerator jg, int level) throws IOException, JsonGenerationException {
            jg.writeRaw(_lf);
            if (level > 0) {
                int numSpaces = level * 4;
                while (numSpaces > LONG_SPACES.length) {
                    jg.writeRaw(LONG_SPACES, 0, LONG_SPACES.length);
                    numSpaces -= LONG_SPACES.length;
                }
                jg.writeRaw(LONG_SPACES, 0, numSpaces);
            }
        }
    }

    public static class Fuga {
        public List<String> strings = Arrays.asList("foo", "bar", "baz");
        public int number = 3;
    }

    public static void with4SpacesIndentation() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper()
                .enable(SerializationFeature.INDENT_OUTPUT);

        String json = objectMapper
                .writer(new DefaultPrettyPrinter()
                        .withObjectIndenter(new Lf4SpacesIndenter()))
                .writeValueAsString(new Fuga());

        System.out.println(json);
    }

やっぱり標準の pretty printer の書式が気持ち悪いので JSON.stringify() ぽくしたい

サンプルコードは長くなってしまったので、Gist に投稿しました。
https://gist.github.com/komiya-atsushi/832a97c84ccae7cdfc2a

実装は非常にだるいのですが、 com.fasterxml.jackson.core.PrettyPrinter を実装してしまえばだいたいなんでもできるようになります。

POJO の null なフィールドは出力しないで JSON にシリアライズしたい

java - How to tell Jackson to ignore a field during serialization if its value is null? - Stack Overflow より。

幾つか方法があります。

その 1 : ObjectMapper に null なフィールドを無視させる設定をする

POJO のクラスをいじりたくない (いじれない) 場合に有効な方法です。

引数に JsonInclude.Include.NON_NULL を指定して、 ObjectMapper#setSerializationInclusion() を呼び出すことで、null なフィールドをシリアライズしない ObjectMapper を得ることができます。

    public static class Fuga {
        public List<String> strings = null; // このフィールドはシリアライズして欲しくない
        public int number = 3;
    }

    public static void ignoreNullFieldByObjectMapper() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper()
                .setSerializationInclusion(JsonInclude.Include.NON_NULL);

        String json = mapper.writeValueAsString(new Fuga());

        System.out.println(json);
    }

実行結果はこちら。

{"number":3}

その 2 : POJO にアノテーションで、null なフィールドはシリアライズ不要である旨を表現させる

ObjectMapper がいろんなところで使われていて、その 1 の対応方法では対処が難しい・面倒な場合にオススメです。

対象の POJO のクラス宣言にて、クラスに対して @JsonInclude(JsonInclude.Include.NON_NULL) のアノテーションを付与します。

    @JsonInclude(JsonInclude.Include.NON_NULL)
    public static class Fuga {
        public List<String> strings = null; // このフィールドはシリアライズして欲しくない
        public int number = 3;
    }

    public static void ignoreNullFieldByAnnotation() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();

        String json = mapper.writeValueAsString(new Fuga());

        System.out.println(json);
    }

列挙型 (enum) のフィールドを、文字列ではなく #ordinal() な値でシリアライズしたい

SerializationConfig.Feature.WRITE_ENUMS_USING_INDEXtrue を指定して、 ObjectMapper#configure() を呼び出します。

    public enum Gender {
        MALE, FEMALE
    }

    public static void serializeUsingEnumOrdinal() throws IOException {
        List<Gender> genders = Arrays.asList(Gender.FEMALE, Gender.MALE);

        String json = new ObjectMapper()
                .configure(SerializationConfig.Feature.WRITE_ENUMS_USING_INDEX, true)
                .writeValueAsString(genders);

        System.out.println(json);
    }