2016-01-11 7 views
9

jaka jest poprawna składnia deserializacji następujący JSON:Jak deserializować JSON do listy <SomeType> z Kotlin + Jackson

[ { 
    "id" : "1", 
    "name" : "Blues" 
}, { 
    "id" : "0", 
    "name" : "Rock" 
} ] 

ja próbowałem:

//Works OK 
val dtos = mapper.readValue(json, List::class.java) 

Jednak chcę:

val dtos : List<GenreDTO> = mapper.readValue(json, 
    List<GenreDTO>::class.java) 

Powyższa składnia jest niepoprawna i daje: only classes are allowed on the left hand side of a class literal

Odpowiedz

15

UWAGA:Odpowiedź z @IRus jest poprawna, to był modyfikowany w tym samym czasie napisałem to wypełnić więcej szczegółów.

Powinieneś użyć Jackson + Kotlin module lub będziesz mieć inne problemy z deserializacją obiektów Kotlin, gdy nie masz domyślnego konstruktora.

Twoja pierwsza próbka kodu:

val dtos = mapper.readValue(json, List::class.java) 

wraca się wywnioskować typ List<*> skoro nie określił więcej informacji o typie i to jest rzeczywiście List<Map<String,Any>> który nie jest tak naprawdę „działa OK”, ale jest nie powoduje żadnych błędów. To jest niebezpieczne, nie wpisane.

Drugi kod powinien być:

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper 
import com.fasterxml.jackson.module.kotlin.readValue 

val mapper = jacksonObjectMapper() 
// ... 
val genres: List<GenreDTO> = mapper.readValue(json) 

Nie trzeba niczego innego po prawej stronie przypisania, moduł Kotlin dla Jacksona będzie zreifikować generyków i stworzyć TypeReference dla Jacksona wewnętrznie. Zwróć uwagę na import readValue, potrzebujesz tego lub .* dla pakietu , aby mieć funkcje rozszerzenia, które wykonują całą magię.

Nieco inna alternatywa, która również działa:

val genres = mapper.readValue<List<GenreDTO>>(json) 

Nie ma powodu, aby nie używać funkcji rozszerzenia oraz dodatkowy moduł dla Jacksona. Jest mały i rozwiązuje inne problemy, które wymagałyby przeskoczenia przez obręcze w celu utworzenia domyślnego konstruktora lub użycia kilku adnotacji. Z modułem Twoja klasa może być normalną Kotlin (opcjonalnie być klasą data):

class GenreDTO(val id: Int, val name: String) 
+1

Używałem modułu Kotlin dla Jacksona i próbowałem czegoś takiego, ale zapomniałem o imporcie. –

+1

Mam nadzieję, że https://youtrack.jetbrains.com/issue/KT-9238 pozwoli bibliotece na opowiedzenie IDE, jakie importowanie jest dla niej ważne. –

4

następujący kod działa dobrze dla mnie:

import com.fasterxml.jackson.databind.ObjectMapper 
import com.fasterxml.jackson.module.kotlin.readValue 
import com.fasterxml.jackson.module.kotlin.registerKotlinModule 



val json = """[ { 
    "id" : "1", 
    "name" : "Blues" 
}, { 
    "id" : "0", 
    "name" : "Rock" 
} ]""" 

data class GenreDTO(val id: Int, val name: String) 

val mapper = ObjectMapper().registerKotlinModule() 

fun main(args: Array<String>) { 
    val obj: List<GenreDTO> = mapper.readValue(json) 
    obj.forEach { 
     println(it) 
    } 
} 

Prace z powodu rozszerzenia funkcji zdefiniowanej wewnątrz Jackson-Kotlin-modułu (który używany reified rodzajowych):

public inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {}) 

Dzięki @JaysonMinard dla zawiadomić ja o tym.

wyjściowa:

GenreDTO(id=1, name=Blues) 
GenreDTO(id=0, name=Rock) 
+1

Ten kod jest niepoprawny. Działa tylko dlatego, że twój typ danych to tablica. Używanie ':: class.java' jest złe ZAWSZE, gdy zaangażowane są generics. –

+0

Dziękuję, @JaysonMinard znalazłem funkcję rozszerzenia, której wcześniej nie zauważyłem. – IRus

4

Błąd dostajesz to o następującym wyrażeniem:

List<GenreDTO>::class.java 

ponieważ, jak JVM traktuje generyków nie ma oddzielne klasy dla List<GenreDTO> więc kompilator narzeka. Podobnie w Javie dodaje nie będzie skompilować:

List<GenreDTO>.getClass() 

Oto próbka, która deserializowania listę prawidłowo:

val value:List<GenreDTO> = mapper.readValue(json, object : TypeReference<List<GenreDTO>>() {}) 

Jak @JaysonMinard wskazał można użyć jackson-module-kotlin uproszczenie inwokację do:

val genres: List<GenreDTO> = mapper.readValue(json) 
// or 
val genres = mapper.readValue<List<GenreDTO>>(json) 

Jest to możliwe z powodu reified type parameters. Rozważ spojrzenie na Extensions, aby dowiedzieć się szczegółów.

+0

Ponieważ powinien używać modułu Jackson Kotlin, należy to uprościć zgodnie z opisem w dokumentacji tego modułu (https://github.com/FasterXML/jackson-module-kotlin). Istnieje prosta próbka do zrobienia właśnie tego. –