Использование 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)