jacksonのデシリアライズ機能のACCEPT_EMPTY_STRING_AS_NULL_OBJECTプロパティについて効果を勘違いしていた。

同様の勘違いをしている人が多々いたのでまとめておく。

環境

  • Java15
  • jackson-dataformat-xml v2.12.2

ACCEPT_EMPTY_STRING_AS_NULL_OBJECTとは

DeserializationFeature (jackson-databind 2.7.0 API)

Javadocにはこのように記載されている。

Feature that can be enabled to allow JSON empty String value ("") to be bound to POJOs as null.

If disabled, standard POJOs can only be bound from JSON null or JSON Object (standard meaning that no custom deserializers or constructors are defined; both of which can add support for other kinds of JSON values);

if enabled, empty JSON String can be taken to be equivalent of JSON null.

Feature is disabled by default.

有効にした場合の説明を直訳すると、「有効にすると、空の JSON 文字列を JSON null と同等と見なすことができます。」となる。

勘違いポイント

空のJSON文字列を単純にStringのnullとして割り当てる機能だと思いこんで勘違いしていた。

正しくは、空のJSON文字列をnullのPOJOとして割り当ててくれる機能である。

(Javadocを詳しく読まずにプロパティ名だけで判断するのは良くない。)

使い方

Student.javaimport com.fasterxml.jackson.annotation.JsonProperty;

public class Student {

    @JsonProperty(namespace = "name")
    private String name;

    @JsonProperty(namespace = "age")
    private int age;

    @JsonProperty(namespace = "address")
    private Address address;

    public String toString() {
        return "Student(name=" + this.name + ", age=" + this.age + ", address=" + this.address + ")";
    }

    static class Address {
        @JsonProperty(namespace = "prefecture")
        private String prefecture;

        @JsonProperty(namespace = "city")
        private String city;

        public String toString() {
            return "Student.Address(prefecture=" + this.prefecture + ", city=" + this.city + ")";
        }
    }
}
Main.javaimport com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;

public class Main {

    public static void main(String[] args) throws JsonProcessingException {
        JsonMapper jsonMapper = new JsonMapper();
        jsonMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

        String jsonString = """
                {
                    "name": "taro",
                    "age": "30",
                    "address": {
                        "prefecture": "東京都",
                        "city": "江東区"
                    }
                }
                """;
        System.out.println(jsonMapper.readValue(jsonString, Student.class));

        String jsonString2 = """
                {
                    "name": "taro",
                    "age": "30"
                }
                """;
        System.out.println(jsonMapper.readValue(jsonString2, Student.class));

        String jsonString3 = """
                {
                    "name": "taro",
                    "age": "30",
                    "address": ""
                }
                """;
        System.out.println(jsonMapper.readValue(jsonString3, Student.class));
    }
}
実行結果Student(name=taro, age=30, address=Student.Address(prefecture=東京都, city=江東区))
Student(name=taro, age=30, address=null)
Student(name=taro, age=30, address=null)

jsonString3のaddressが空文字であるが、デシリアライズ時にaddressフィールドをnullでマッピングしてくれる。

ACCEPT_EMPTY_STRING_AS_NULL_OBJECTを有効にしない場合、jsonString3のデシリアライズ実行時に例外がスローされる。

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot coerce empty String ("") to `jackson.Student$Address` value (but could if coercion was enabled using `CoercionConfig`)
 at [Source: (String)"{
    "name": "taro",
    "age": "30",
    "address": ""
}