Como usar TypeToken + genéricos com Gson em Kotlin


108

Não consigo obter uma lista de tipo genérico de uma classe personalizada (Turns):

val turnsType = TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson(pref.turns, turnsType)

disse:

cannot access '<init>' it is 'public /*package*/' in 'TypeToken'

Respostas:


236

Crie esta diversão inline:

inline fun <reified T> Gson.fromJson(json: String) = fromJson<T>(json, object: TypeToken<T>() {}.type)

e então você pode chamá-lo desta forma:

val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)

Alternativas anteriores:

ALTERNATIVA 1:

val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)

Você tem que colocar object :e o tipo específico emfromJson<List<Turns>>


ALTERNATIVA 2:

Como menção @cypressious, também pode ser alcançado desta forma:

inline fun <reified T> genericType() = object: TypeToken<T>() {}.type

usar como:

val turnsType = genericType<List<Turns>>()

4
Você também pode criar um método auxiliar que faça isso para você:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
— Kirill Rakhman,

1
ou até mesmo estende Gson para ter uma nova sobrecarga de fromJson que faz isso. Kotlin foi projetado para estender, então estenda Gson para torná-lo mais agradável e ocultar TypeTokens.
— Jayson Minard

Fiz uma sugestão de edição que torna a resposta mais completa e formal, uma vez que essa resposta provavelmente será vista por muitos que usam Gson. Eu adicionei explicações na resposta e links para referências do Kotlin para tópicos usados ​​para resolver o problema ... para que as pessoas não precisem ler todas as outras respostas ou comentários que entraram nisso. Se você aceitar a edição, posso remover minha resposta abaixo.
— Jayson Minard

Editar rejeitado, veja minha resposta abaixo para uma versão completa que combina todas as respostas e comentários em uma resposta coerente. Você aceitou sua própria resposta, mas ela não está completa.
— Jayson Minard

removendo aviso de kotlin: fun inline <reified T> genericType (): Type? = objeto: TypeToken <T> () {} .type
— Juan Mendez

28

Isso resolve o problema:

val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)

A primeira linha cria uma expressão de objeto que desce TypeTokene obtém o Java a Typepartir dela . Então, o Gson().fromJsonmétodo precisa do tipo especificado para o resultado da função (que deve corresponder ao TypeTokencriado). Duas versões deste trabalho, como acima ou:

val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)

Para facilitar a criação de, TypeTokenvocê pode criar uma função auxiliar, que deve estar embutida para que possa usar parâmetros de tipo reificado :

inline fun <reified T> genericType() = object: TypeToken<T>() {}.type

Que pode então ser usado de uma destas maneiras:

val turnsType = genericType<List<Turns>>()
// or
val turnsType: List<Turns> = genericType()

E todo o processo pode ser envolvido em uma função de extensão para a Gsoninstância:

inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type)

Para que você possa apenas ligar para Gson e não se preocupar com TypeTokennada:

val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)

Aqui, Kotlin está usando inferência de tipo de um lado da atribuição ou do outro, e genéricos reificados para que uma função inline passe pelo tipo completo (sem apagamento) e usando isso para construir um TypeTokene também fazer a chamada para Gson


1
Olá @Jayson, não consegui fazer com que funcionasse com tanta diversão embutida no Android Studio. Parece estar bem, mas não é reconhecido quando eu façoGson().fromJson<kotlin.List<Turns>>(pref.turns)
— Juan Saravia

@juancho, você pode me dizer o que significa "não reconhecido"? Um erro do compilador? Você tem o método de extensão importado e disponível acima?
— Jayson Minard

2
Copiei e colei seu código no Android Studio e importei a diversão para minha aula de kotlin. Tentei fazer o que você disse, mas por algum motivo o compilador me disse que essa diversão não existe. Já estou usando outras funções de extensão, mas não sei o que sua sugestão não funciona. Qual versão do AS e Kotlin você está usando? apenas para tentar novamente.
— Juan Saravia

Isso não está relacionado diretamente ao Android Studio, Kotlin é o mesmo dentro ou fora dele. Você está criando instância de Gson()ou apenas Gsoncomo se fosse estático? Você precisa do primeiro, uma instância.
— Jayson Minard

21

Outra opção (não tenho certeza se parece mais elegante do que as outras) pode ser uma chamada como esta:

turns = Gson().fromJson(allPurchasesString, Array<Turns>::class.java).toMutableList()

Portanto, você está usando o liner de classe um do java Array em vez de "Kotlin puro".


2
Como o TypeToken não funciona em todos os telefones confiáveis, esta é a melhor solução para mim. Um forro fácil com kotlin puro.
— LuckyMalaka

11
val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
    object : TypeToken<List<SaleItemResponse>>() {}.type)

É a minha maneira de analisar o array de dados em kotlin.


Esta deve ser a resposta aceita, curta e simples.
— Umar Ata

6

Eu usei algo como isto para converter Tpara stringe Stringvolta a Tutilizar Gson. Não exatamente o que você está procurando, mas apenas no caso.

Declarando extensão

inline fun <reified T : Any> T.json(): String = Gson().toJson(this, T::class.java)
inline fun <reified T : Any> String.fromJson(): T = Gson().fromJson(this,T::class.java)

Uso

// Passing an object to new Fragment
companion object {    
        private const val ARG_SHOP = "arg-shop"

        @JvmStatic
        fun newInstance(shop: Shop) =
                ShopInfoFragment().apply {
                    arguments = Bundle().apply {
                        putString(ARG_SHOP, shop.json())
                    }
                }
    }

// Parsing the passed argument
private lateinit var shop: Shop

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            shop = it.getString(ARG_SHOP).fromJson() ?: return
        }
    }

5

Isso também funciona e é mais simples

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.java)

O tipo de retorno deve ser anulável. Caso contrário, o código Java na biblioteca Gson pode retornar nulo, mas Kotlin assume que o tipo não pode ser nulo. Como resultado, você obtém NPE em Kotlin.
— fdermishin

1

Kotlin generic reified functionde Gson desserializa para ArrayList<T>usar este código

 inline fun <reified T> get( ... ): ArrayList<T>{
    
    val str = "[{},{}]"
    
    val type = TypeToken.getParameterized(ArrayList::class.java, T::class.java).type
    
    val t = Gson().fromJson<ArrayList<T>>(str, type)
    

    return t
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.