Cara menggunakan TypeToken + generik dengan Gson di Kotlin

108

Saya tidak bisa mendapatkan Daftar tipe generik dari kelas khusus (Putaran):

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

itu berkata:

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

Jawaban:

236

Buat kesenangan sebaris ini:

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

dan kemudian Anda dapat menyebutnya dengan cara ini:

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

Alternatif Sebelumnya:

ALTERNATIF 1:

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

Anda harus memasukkan object :dan jenis tertentu masukfromJson<List<Turns>>


ALTERNATIF 2:

Seperti yang disebut @cypressious, hal itu dapat dicapai juga dengan cara ini:

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

digunakan sebagai:

val turnsType = genericType<List<Turns>>()
Juan Saravia
sumber
4
Anda juga dapat membuat metode pembantu yang melakukannya untuk Anda:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Kirill Rakhman
1
atau bahkan memperluas Gson untuk memiliki kelebihan baru fromJson yang melakukan ini. Kotlin dirancang untuk diperluas, jadi perluas Gson agar lebih bagus dan sembunyikan TypeTokens.
Jayson Minard
Saya membuat pengeditan yang disarankan agar jawaban lebih lengkap dan formal karena jawaban ini cenderung dilihat oleh banyak pengguna Gson. Saya menambahkan penjelasan dalam jawaban, dan tautan ke referensi Kotlin untuk topik yang digunakan untuk memecahkan masalah ... sehingga orang tidak perlu membaca semua jawaban atau komentar lain yang membahas ini. Jika Anda menerima edit, saya dapat menghapus jawaban saya di bawah.
Jayson Minard
Edit ditolak, lihat jawaban saya di bawah untuk versi lengkap yang menggabungkan semua jawaban dan komentar menjadi satu jawaban yang koheren. Anda menerima jawaban Anda sendiri tetapi belum lengkap.
Jayson Minard
menghapus peringatan kotlin: kesenangan inline <reified T> genericType (): Type? = object: TypeToken <T> () {} .type
Juan Mendez
28

Ini menyelesaikan masalah:

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

Baris pertama membuat ekspresi objek yang diturunkan dari TypeTokendan kemudian mendapatkan Java Typedarinya. Kemudian Gson().fromJsonmetode tersebut membutuhkan tipe yang ditentukan untuk hasil fungsi (yang harus cocok dengan yang TypeTokendibuat). Dua versi dari karya ini, seperti di atas atau:

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

Untuk mempermudah pembuatannya, TypeTokenAnda bisa membuat fungsi helper, yang harus inline agar bisa menggunakan parameter tipe reified :

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

Yang kemudian dapat digunakan dengan salah satu cara berikut:

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

Dan seluruh proses dapat digabungkan menjadi fungsi ekstensi untuk Gsoncontoh:

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

Sehingga Anda dapat menelepon Gson dan tidak khawatir tentang hal tersebut TypeToken:

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

Di sini Kotlin menggunakan inferensi tipe dari satu sisi tugas atau sisi lainnya, dan reifikasi generik untuk fungsi sebaris agar melewati tipe lengkap (tanpa penghapusan), dan menggunakannya untuk membuat TypeTokendan juga melakukan panggilan ke Gson

Jayson Minard
sumber
1
Hai @Jayson, saya tidak dapat membuatnya berfungsi dengan menyenangkan sebaris ini di Android Studio. Tampaknya baik-baik saja tetapi tidak dikenali ketika saya melakukannyaGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia
@juancho bisakah kamu meneleponku apa artinya "tidak dikenali"? Kesalahan kompiler? Anda memiliki metode ekstensi yang diimpor dan tersedia dari atas?
Jayson Minard
2
Saya menyalin dan menempelkan kode Anda di Android Studio dan mengimpor kesenangan di kelas kotlin saya. Saya mencoba melakukan apa yang Anda katakan tetapi untuk beberapa alasan kompilator memberi tahu saya bahwa kesenangan ini tidak ada. Saya sudah menggunakan fungsi ekstensi lain tetapi saya tidak tahu saran Anda tidak berfungsi. Versi AS dan Kotlin mana yang Anda gunakan? hanya untuk mencobanya lagi.
Juan Saravia
Ini tidak terkait langsung dengan Android studio, Kotlin sama di dalam atau di luarnya. Apakah Anda membuat instance Gson()atau Gsonseolah-olah itu statis? Anda membutuhkan yang pertama, sebuah contoh.
Jayson Minard
21

Opsi lain (tidak yakin tampilannya lebih elegan dari yang lain) mungkin panggilan seperti ini:

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

Jadi, Anda menggunakan liner java Array class one daripada "Kotlin murni".

Tobias Reich
sumber
2
Karena TypeToken tidak berfungsi pada setiap telepon yang dapat diandalkan, ini adalah solusi terbaik untuk saya. Satu liner yang mudah dengan kotlin murni.
LuckyMalaka
11
val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
    object : TypeToken<List<SaleItemResponse>>() {}.type)

Ini cara saya untuk mem-parsing larik data di kotlin.

Toàn Mỹ
sumber
Ini harus menjadi jawaban yang diterima, singkat dan sederhana.
Umar Ata
6

Saya menggunakan sesuatu seperti ini untuk mengubah Tke string& Stringkembali Tmenggunakan Gson. Bukan apa yang Anda cari, tetapi untuk berjaga-jaga.

Mendeklarasikan ekstensi

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)

Pemakaian

// 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
        }
    }
harsh_v
sumber
5

Ini bekerja dengan baik, dan lebih sederhana

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.java)
Christopher Perry
sumber
Jenis pengembalian harus nihil. Jika tidak, kode Java di pustaka Gson bisa menghasilkan null, tetapi Kotlin menganggap jenisnya bukan nullable. Hasilnya, Anda mendapatkan NPE di Kotlin.
fdermishin
1

Kotlin generic reified functiondari Gson deserialize untuk ArrayList<T>menggunakan kode ini

 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
}
Muslim Shahsavan
sumber