Reputation: 3683
I was trying to use Jackson to write a class value to JSON that has Optional as fields:
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()));
}
}
When executed, this class generates the following output:
{"field":{"present":true}}
I understand the present/not present field being included and could work around it when reading the JSON data, however I can't get around the fact that the actual content of the optional is never written to the output. :(
Any workarounds here except not using ObjectMapper at all?
Upvotes: 75
Views: 88379
Reputation: 3020
You could use jackson-datatype-jdk8 which is described as:
Support for new JDK8-specific types, such as Optional
In order to do this:
com.fasterxml.jackson.datatype:jackson-datatype-jdk8
as a dependencyobjectMapper.registerModule(new Jdk8Module());
Upvotes: 79
Reputation: 2757
You only need to register module Jdk8Module
. Then it will serialize any Optional<T>
as T
if it is present or null
otherwise.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
Let's modify Test
class a bit so we can test for an Optional.empty()
value. This is similar to the original Test
class when serialized because the object mapper is looking for the getter (since the field is private
). Using the original Test
class will work too, though.
class Test {
private final String field;
public Test(String field) {
this.field = field;
}
public Optional<String> getField() {
return Optional.ofNullable(field);
}
}
Then in our main class:
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));
Upvotes: 16
Reputation: 971
Try passing in options into @JsonInclude
annotation.
For example, if you don't want to show field
when the value is null
. You might need to use Jackson-Modules >2.8.5.
import com.fasterxml.jackson.annotation.JsonInclude;
public class Test {
@JsonInclude(JsonInclude.Include.NON_NULL)
Optional<String> field;
}
Upvotes: 6
Reputation: 464
If you are using latest version of Spring-boot then you could achieve this by adding the following dependency in the pom file
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
And auto wire the JacksonObjectMapper.
@Autowired
private ObjectMapper jacksonObjectMapper;
Then use the above Spring container's mapper instance to convert Object to String
jacksonObjectMapper.writeValueAsString(user);
Some well known Jackson modules are automatically registered if they are detected on the classpath:
- jackson-datatype-jdk7: Java 7 types like java.nio.file.Path (as of 4.2.1 release)
- jackson-datatype-joda: Joda-Time types
- jackson-datatype-jsr310: Java 8 Date & Time API data types
- jackson-datatype-jdk8: other Java 8 types like Optional (as of 4.2.0 release)
Upvotes: 3
Reputation: 3174
Similar to @Manikandan's answer but add @JsonProperty
to the private field instead of a getter so you don't expose your work around on the public api.
public class Test {
@JsonProperty("field")
private String field;
@JsonIgnore
public Optional<String> getField() {
return Optional.of(field); // or Optional.ofNullable(field);
}
}
Upvotes: 17
Reputation: 280102
The Optional
class has a value
field, but no standard getter/setter for it. By default, Jackson looks for getters/setters to find class properties.
You can add a custom Mixin to identify the field as a property
final class OptionalMixin {
private Mixin(){}
@JsonProperty
private Object value;
}
and register it with your ObjectMapper
.
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Optional.class, OptionalMixin.class);
You can now serialize your object.
System.out.println(mapper.writeValueAsString(new Test()));
will print
{"field":{"value":"hello, world!","present":true}}
Consider also looking at jackson-datatype-guava
. There's a Jackson Module
implementation for Guava types including their Optional
. It's possibly more complete than what I've shown above.
Upvotes: 15
Reputation: 3165
Define new getter which will return String instead of 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);
}
}
Upvotes: 3