Использование Jackson ObjectMapper с Java 8 Дополнительные значения
Я пытался использовать Jackson для записи значения класса в 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}}
Я понимаю, что настоящее/не существующее поле включено и может работать вокруг него при чтении данных JSON, однако я не могу обойти тот факт, что фактическое содержимое необязательного никогда не записывается на выход.: (
Какие-либо обходные пути здесь, если не использовать ObjectMapper?
Ответы
Ответ 1
Вы можете использовать jackson-datatype-jdk8, который описывается как:
Поддержка новых JDK8-специфических типов, таких как Optional
Для этого:
- добавить
com.fasterxml.jackson.datatype:jackson-datatype-jdk8
в качестве зависимости - зарегистрируйте модуль в вашем объектном
objectMapper.registerModule(new Jdk8Module());
: objectMapper.registerModule(new Jdk8Module());
Ответ 2
Класс Optional
имеет поле value
, но для него нет стандартного геттера/сеттера. По умолчанию Джексон ищет геттеры/сеттеры, чтобы найти свойства класса.
Вы можете добавить собственный 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
. Там реализована реализация Jackson Module
для типов Guava, включая их Optional
. Возможно, это более полно, чем показано выше.
Ответ 3
Аналогично @Manikandan ответьте, но добавьте @JsonProperty
в личное поле вместо получателя, чтобы вы не раскрывали свою работу на публичном api.
public class Test {
@JsonProperty("field")
private String field;
@JsonIgnore
public Optional<String> getField() {
return Optional.of(field); // or Optional.ofNullable(field);
}
}
Ответ 4
Вам нужно только зарегистрировать модуль Jdk8Module
. Затем он будет сериализовать любой Optional<T>
как T
если он присутствует, или null
противном случае.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
Давайте немного Test
класс 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));
Ответ 5
Попробуйте передать варианты в аннотацию @JsonInclude
.
Например, если вы не хотите показывать field
, когда значение null
. Возможно, вам придется использовать Jackson-Modules > 2.8.5.
import com.fasterxml.jackson.annotation.JsonInclude;
public class Test {
@JsonInclude(JsonInclude.Include.NON_NULL)
Optional<String> field;
}
Ответ 6
Определите новый 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);
}
}
Ответ 7
Если вы используете последнюю версию 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 блог говорит:
Некоторые хорошо известные модули Джексона автоматически регистрируются, если они обнаружены в пути к классам:
- jackson-datatype-jdk7: типы Java 7, такие как java.nio.file.Path(начиная с версии 4.2.1)
- jackson-datatype-joda: типы Joda-Time
- jackson-datatype-jsr310: типы данных API по дате и времени Java 8
- jackson-datatype-jdk8: другие типы Java 8, такие как необязательные (начиная с версии 4.2.0)