2014-09-05 19 views
37

starałem się używać Jackson napisać wartość klasy do formatu JSON, który ma Opcjonalnie jako dziedzinach:Korzystanie Jackson ObjectMapper z Java 8 Wartości opcjonalne

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())); 
    } 
} 

Po wykonaniu tej klasy generuje następujący wynik:

{"field":{"present":true}} 

Rozumiem, że obecne/nieobecne pole jest zawarte i może obejść go podczas odczytu danych JSON, jednak nie mogę obejść faktu, że rzeczywista zawartość opcjonalna nigdy nie jest zapisywana na wyjściu. :(

żadnego obejścia tutaj wyjątkiem nie używając ObjectMapper

+9

opcjonalne nie są przeznaczone do stosowania jako pola (lub właściwości, o to chodzi). Powinny być używane tylko jako wartości zwracane. – zeroflagL

+11

@zeroflagL Czy mógłbyś podać jakieś wiarygodne źródła opisujące, co Optional ma być? – Jonas

+6

@ Jonas [ta odpowiedź] (http://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type/26328555#26328555) np .: _Naszą intencją było zapewnienie ograniczonego mechanizmu dla biblioteki metody zwracania typów, w których musi istnieć wyraźny sposób przedstawienia "brak wyniku" _. Również niewprowadzanie 'Serializable' jest całkiem oczywiste. – zeroflagL

Odpowiedz

42

Można użyć jackson-datatype-jdk8 która jest opisana jako:

obsługę nowych typów JDK8 specyficznych, takich jak opcjonalne

+2

Zgodnie z połączonym repozytorium GitHub: 'UWAGA: Ten moduł stał się częścią Jackson Java 8 Modules od Jackson 2.8.5 repo istnieje, aby umożliwić wydanie łatek starszych wersji; będzie ukryty (zrobiony prywatnie) w najbliższej przyszłości. " – Carsten

+1

Musisz również zarejestrować moduł taki jak ' '' ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule (nowy Jdk8Module()); '' ' – harsh

+0

To jest teraz w https://github.com/FasterXML/jackson-modules-java8 –

3

w ogóle? Definiować nowe getter która będzie zwracać String zamiast opcjonalne.

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

To zadziała, tak, ale w pierwszej kolejności zniszczy cel lub zwróci wartość opcjonalną. – asieira

12

Klasa Optional ma pole value, ale nie standardowy getter/setter dla niego Domyślnie, Jackson poszukuje podmiotów pobierających/ustawiających, aby znaleźć właściwości klas.

Możesz dodać niestandardowy Mixin, aby zidentyfikować pole jako właściwość

final class OptionalMixin { 
    private Mixin(){} 
    @JsonProperty 
    private Object value; 
} 

i zarejestruj go pod numerem ObjectMapper.

ObjectMapper mapper = new ObjectMapper(); 
mapper.addMixInAnnotations(Optional.class, OptionalMixin.class); 

Można teraz serializować obiekt.

System.out.println(mapper.writeValueAsString(new Test())); 

wypisze

{"field":{"value":"hello, world!","present":true}} 

również rozważyć patrząc na jackson-datatype-guava. Istnieje implementacja Jacksona Module dla typów Guava, w tym ich Optional. To może być bardziej kompletne niż to, co pokazałem powyżej.

+0

To działa, dziękuję. Czy istnieje sposób na uczynienie tego Mixin domyślnym dla wszystkich nowych twórców map? – asieira

+1

@asieira Uważam, że musisz zarejestrować 'Module', który rejestruje mixin. Następnie należy utworzyć program odwzorowujący za pomocą tego 'Module'.(Nie wiem, czy pasuje ci to lepiej niż rejestrowanie mixina.) –

6

podobne do @ odpowiedź Manikandan, ale dodać @JsonProperty na polu prywatnym, zamiast getter, więc nie wystawiaj swojej pracy na publiczne api.

3

Spróbuj przekazać opcje do adnotacji @JsonInclude.
Na przykład, jeśli nie chcesz wyświetlać field, gdy wartość wynosi null. Być może będziesz musiał użyć Jackson-Modules> 2.8.5.

import com.fasterxml.jackson.annotation.JsonInclude; 

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

To jest dokładnie to, czego szukałem i co prawdopodobnie powinno być odpowiedzią. –

0

Jeśli używasz najnowszej wersji Wiosna-boot to można to osiągnąć dodając następującą zależność w pliku pom

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

i auto drutu JacksonObjectMapper.

@Autowired 
private ObjectMapper jacksonObjectMapper; 

Następnie użyj instancji odwzorowujący powyższych Wiosna kontenera konwertować obiekt String

jacksonObjectMapper.writeValueAsString(user); 

Spring blog says :

Niektóre dobrze znane moduły Jackson są automatycznie rejestrowane, jeżeli są one wykryte na ścieżce klasy:

  • Jackson typ danych, jdk7: JAVA 7 typów jak java.nio.file.Path (od 4.2.1 uwalnianiu)
  • Jackson-JODA typ danych: Typ Joda czasie
  • Jackson typ danych, jsr310: JAVA 8 Data typy danych API & Czas
  • Jackson-datatype-jdk8: Java 8 typów, takich jak opcjonalne (poczynając od wersji 4.2.0)
0

wystarczy tylko zarejestrować moduł Jdk8Module. Następnie będzie serializować dowolny Optional<T> jako T, jeśli jest obecny lub w innym przypadku.

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

Zmodyfikujmy Test klasę nieco więc możemy przetestować dla wartości Optional.empty(). Jest to podobne do oryginalnej klasy Test, gdy jest serializowana, ponieważ obiekt odwzorowujący szuka pozyskiwania (ponieważ pole to jest private). Używanie oryginalnej klasy Test również będzie działać.

class Test { 
    private final String field; 

    public Test(String field) { 
     this.field = field; 
    } 

    public Optional<String> getField() { 
     return Optional.ofNullable(field); 
    } 
} 

Następnie w głównej klasie:

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)); 
Powiązane problemy