2014-09-05 3 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

Optionals не предназначены для использования в качестве полей (или свойств этого вещества). Они должны использоваться только как возвращаемые значения. – zeroflagL

+11

@zeroflagL Вы могли бы предоставить любые достоверные источники, описывающие, что такое необязательный вариант? – Jonas

+6

@Jonas [этот ответ] (http://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555) например: _Наше намерение состояло в том, чтобы предоставить ограниченный механизм для библиотеки методы возвращают типы, где должен быть четкий способ представления «никакого результата» _. Также не реализация «Serializable» довольно очевидна. – zeroflagL

ответ

42

Вы можете использовать jackson-datatype-jdk8, который описан как:

Поддержка новых JDK8 специфических типов, таких как Факультативный

+2

В соответствии с связанным репозиторием GitHub: 'ПРИМЕЧАНИЕ: Этот модуль стал частью модулей Jackson Java 8 от Jackson 2.8.5 Это repo все еще существует, чтобы разрешить выпуск патч-версий более старых версий; он будет скрыт в ближайшее время. « – Carsten

+1

Вам также необходимо зарегистрировать модуль, например « Объект ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule (новый Jdk8Module()); '' ' – harsh

+0

Теперь он находится в https://github.com/FasterXML/jackson-modules-java8 –

3

Определить новый поглотитель, который будет возвращать строку вместо необязательного.

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

12

Optional класса имеет value поля, но не стандартный геттерные/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. Существует реализация Jackson Module для типов Guava, включая их Optional. Возможно, это более полно, чем показано выше.

+0

Это работает, спасибо. Есть ли способ сделать этот Mixin по умолчанию для всех новых mappers? – asieira

+1

@asieira Я считаю, что вам нужно зарегистрировать 'Module', который регистрирует mixin. Затем вы должны создать картупер с помощью этого «модуля».(Я не знаю, подходит ли это вам лучше, чем только регистрация mixin.) –

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 аннотация.
Например, если вы не хотите показывать field, когда значение null. Возможно, вам придется использовать Jackson-Modules> 2.8.5.

import com.fasterxml.jackson.annotation.JsonInclude; 

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

Это именно то, что я искал и, вероятно, должен быть ответом. –

0

Если вы используете последнюю версию Spring-загрузки, то вы могли бы добиться этого, добавив следующую зависимость в файле пом

<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) выпуска
  • джексон-тип данных-Joda: типы Joda-Time
  • джексон-тип данных-jsr310: Java-8 Дата & Время типов данных API
  • ДЖЕКСОН-тип_данный-jdk8: другие Ja 8 типов, такие как факультативные (по состоянию на 4.2.0 выпуска)
0

Вам нужно только зарегистрировать модуль Jdk8Module. Затем он будет сериализовать любые Optional<T> как T, если он присутствует, или null в противном случае.

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

Давайте изменим класс Test немного, чтобы мы могли проверить для Optional.empty() значения. Это похоже на исходный класс Test при сериализации, потому что объект mapper ищет геттер (поскольку поле 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));