使用Jackson ObjectMapper和Java 8可选值

时间:2014-09-05 20:12:06

标签: java jackson java-8

我试图使用Jackson将类值写入JSON,其中包含Optional作为字段:

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}}

我理解当前/不存在的字段被包含在内并且可以在读取JSON数据时解决它,但是我无法解决可选的实际内容永远不会写入输出的事实。 :(

除了根本不使用ObjectMapper之外,这里有任何变通方法吗?

7 个答案:

答案 0 :(得分:52)

您可以使用jackson-datatype-jdk8,其描述为:

  

支持新的JDK8特定类型,例如Optional

为了做到这一点:

  • com.fasterxml.jackson.datatype:jackson-datatype-jdk8添加为依赖项
  • 使用对象映射器注册模块:objectMapper.registerModule(new Jdk8Module());

答案 1 :(得分:13)

Optional类有一个value字段,但没有标准的getter / setter。默认情况下,杰克逊寻找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-guava。对于Guava类型,杰克逊Module实现了Optional。它可能比我上面所示的更完整。

答案 2 :(得分:9)

与@ Manikandan的回答类似,但是将@JsonProperty添加到私有字段而不是getter,这样您就不会在公共API上公开您的工作。

public class Test {

    @JsonProperty("field")
    private String field;

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

答案 3 :(得分:5)

您只需注册模块Jdk8Module即可。然后它会将Optional<T>序列化为T(如果存在)或null

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

让我们稍微修改一下Test类,这样我们就可以测试Optional.empty()值。这与序列化时的原始Test类相似,因为对象映射器正在查找getter(因为该字段为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)); 

答案 4 :(得分:3)

定义新的getter,它将返回String而不是Optional。

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);
    }

}

答案 5 :(得分:3)

尝试将选项传入@JsonInclude注释 例如,如果您不希望在值为field时显示null您可能需要使用Jackson-Modules&gt; 2.8.5

import com.fasterxml.jackson.annotation.JsonInclude;

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

答案 6 :(得分:1)

如果您使用的是最新版本的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);

Spring blog says :

  

如果在类路径中检测到一些众所周知的Jackson模块,则会自动注册:

     
      
  • jackson-datatype-jdk7:Java 7类型,如java.nio.file.Path(自4.2.1版本起)
  •   
  • jackson-datatype-joda:Joda-Time types
  •   
  • jackson-datatype-jsr310:Java 8 Date&amp;时间API数据类型
  •   
  • jackson-datatype-jdk8:其他Java 8类型,如Optional(自4.2.0发布)
  •