web-dev-qa-db-ja.com

Jackson ObjectMapperをJava 8オプション値で使用する

Jacksonを使用して、フィールドとしてOptionalを持つJSONにクラス値を書き込もうとしていました。

public class Test {
    Optional<String> field = Optional.of("hello, world!");

    public Optional<String> getField() {
        return field;
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(new Test()));
    }
}

このクラスを実行すると、次の出力が生成されます。

{"field":{"present":true}}

Present/not presentフィールドが含まれていることを理解しており、JSONデータを読み取るときに回避できますが、オプションの実際のコンテンツが出力に書き込まれないという事実を回避することはできません。 :(

ObjectMapperをまったく使用しないことを除いて、ここで回避策はありますか?

49
asieira

jackson-datatype-jdk8 を使用できます。

オプションなど、新しいJDK8固有のタイプのサポート

これを行うためには:

  • _com.fasterxml.jackson.datatype:jackson-datatype-jdk8_を依存関係として追加します
  • モジュールをオブジェクトマッパーに登録します:objectMapper.registerModule(new Jdk8Module());
56
Jonas

Optionalクラスにはvalueフィールドがありますが、標準のゲッター/セッターはありません。デフォルトでは、Jacksonはクラスプロパティを見つけるためにgetter/setterを探します。

カスタムMixinを追加して、フィールドをプロパティとして識別することができます

final class OptionalMixin {
    private Mixin(){}
    @JsonProperty
    private Object value;
}

ObjectMapperに登録します。

ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Optional.class, OptionalMixin.class);

これで、オブジェクトをシリアル化できます。

System.out.println(mapper.writeValueAsString(new Test()));

印刷します

{"field":{"value":"hello, world!","present":true}}

jackson-datatype-guavaModuleを含むGuava型のJackson Optional実装があります。おそらく、上記で示したものよりも完全なものです。

13

@Manikandanの回答に似ていますが、@JsonPropertyゲッターではなくプライベートフィールドに追加して、パブリックAPIでの作業を公開しないようにします。

public class Test {

    @JsonProperty("field")
    private String field;

    @JsonIgnore
    public Optional<String> getField() {
        return Optional.of(field); // or Optional.ofNullable(field);
    }
}
9
mjj1409

モジュール_Jdk8Module_を登録するだけです。次に、_Optional<T>_が存在する場合はTとして、そうでない場合はnullとしてシリアル化します。

_ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
_

Optional.empty()値をテストできるように、Testクラスを少し変更してみましょう。これは、オブジェクトマッパーがゲッターを探しているため(フィールドがTestであるため)、シリアル化されたときの元のprivateクラスに似ています。ただし、元のTestクラスを使用しても機能します。

_class Test {
    private final String field;

    public Test(String field) {
        this.field = field;
    }

    public Optional<String> getField() {
        return Optional.ofNullable(field);
    }
}
_

次に、メインクラスで:

_Test testFieldNull = new Test(null);
Test testFieldNotNull = new Test("foo");

// output: {"field":null}
System.out.println(objectMapper.writeValueAsString(testFieldNull)); 

// output: {"field":"foo"}
System.out.println(objectMapper.writeValueAsString(testFieldNotNull)); 
_
6
cakraww

OptionalではなくStringを返す新しいゲッターを定義します。

public class Test {
    Optional<String> field = Optional.of("hello, world!");

    @JsonIgnore
    public Optional<String> getField() {
        return field;
    }

    @JsonProperty("field")
    public String getFieldName() {
        return field.orElse(null);
    }

}
3
Manikandan

オプションを@JsonIncludeアノテーションに渡してみてください。
たとえば、値がfieldのときにnullを表示したくない場合。 2.8.5以上のJackson-Modulesを使用する必要がある場合があります

import com.fasterxml.jackson.annotation.JsonInclude;

public class Test {
    @JsonInclude(JsonInclude.Include.NON_NULL)
    Optional<String> field;
}
3
jiantongc

Spring-bootの最新バージョンを使用している場合、pomファイルに次の依存関係を追加することでこれを実現できます。

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

JacksonObjectMapperを自動配線します。

@Autowired
private ObjectMapper jacksonObjectMapper;

次に、上記のSpringコンテナのマッパーインスタンスを使用して、ObjectをStringに変換します

jacksonObjectMapper.writeValueAsString(user);

春のブログは言う:

いくつかのよく知られているジャクソンモジュールは、クラスパスで検出されると自動的に登録されます。

  • jackson-datatype-jdk7:Java Java.nio.file.Pathのような7タイプ(4.2.1リリース時点)
  • jackson-datatype-joda:Joda-Timeタイプ
  • jackson-datatype-jsr310:Java 8 Date&Time APIデータ型
  • jackson-datatype-jdk8:other Java Optionalなどの8つのタイプ(4.2.0リリース以降)
1
seenimurugan