2014-09-05 72 views
37

我试图用杰克逊写一个类值JSON具有可选的字段:使用杰克逊ObjectMapper与Java 8可选值

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呢?

+9

选配并不意味着用作域(或与此有关的属性)。它们只能用作返回值。 – zeroflagL 2014-09-06 09:28:31

+11

@zeroflagL您能否提供任何可信来源来描述可选的含义? – Jonas 2014-12-03 12:54:57

+6

@Jonas [这个答案](http://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555)例如:_我们的目的是为图书馆提供一个有限的机制方法返回类型,其中需要有一种清晰的方式来表示“无结果”_。还没有实现'Serializable'是非常不言而喻的。 – zeroflagL 2014-12-04 07:03:41

回答

42

你可以使用它被描述为jackson-datatype-jdk8

支持新的特定JDK8类型,如可选

+2

根据链接的GitHub存储库: '注意:这个模块已经成为杰克逊Java 8模块的一部分,截至Jackson 2.8.5 This回购仍然存在,以允许发布旧版本的补丁版本;它将在不久的将来被隐藏(私有)。“# – Carsten 2016-11-08 08:22:38

+1

您还需要注册一个模块,如 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Jdk8Module());''' – harsh 2017-11-08 11:35:42

+0

它现在在https://github.com/FasterXML/jackson-modules-java8 – 2018-02-06 09:33:27

3

定义新的getter将返回,而不是可选的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); 
    } 

} 
+6

这会工作,是的,但会打败目的或首先返回一个可选值。 – asieira 2014-09-07 15:04:58

12

Optional类有一个value领域,但没有标准的getter默认情况下,Jackson会寻找获取者/设置者来查找类属性

您可以添加一个自定义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类型,包括Optional,有一个Jackson Module实现。这可能比我上面显示的更完整。

+0

工作,谢谢。有没有办法让Mixin成为所有新映射器的默认设置? – asieira 2014-09-07 15:03:32

+1

@asieira我相信你需要注册'Module'来注册mixin。您必须使用此“模块”创建映射器。(我不知道这是否适合你,而不仅仅是注册mixin。) – 2014-09-07 15:05:30

6

类似@ Manikandan的答案,但添加@JsonProperty到私人领域,而不是一个吸气剂,所以你不会公开你的工作在公共api上。

public class Test { 

    @JsonProperty("field") 
    private String field; 

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

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

import com.fasterxml.jackson.annotation.JsonInclude; 

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

这正是我正在寻找和可能应该是答案。 – 2017-01-25 15:33:33

0

如果您正在使用最新版本的春天启动的,那么你可以通过在POM文件

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

和自动线的JacksonObjectMapper添加以下的依赖实现这一目标。

@Autowired 
private ObjectMapper jacksonObjectMapper; 

然后用上面的Spring容器的映射器实例对象转换为字符串

jacksonObjectMapper.writeValueAsString(user); 

Spring blog says :

,如果他们在类路径中检测到一些著名的杰克逊模块将自动注册:

  • 杰克逊 - 数据类型-JDK7:Java 7种的类型,如java.nio.file.Path(如4.2.1释放的)
  • 杰克逊 - 数据类型-乔达:约达时类型
  • 杰克逊 - 数据类型-jsr310:8的Java日期&时间API数据类型
  • 杰克逊的数据类型,jdk8:其他Java 8种可选一样(如图4.2.0版本的)
0

你只需要注册模块Jdk8Module。然后它将序列化任何Optional<T>作为T,如果它存在或null否则。

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

让我们修改Test类了一点,所以我们可以测试的Optional.empty()值。这与序列化时原始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));