วิธีใช้ TypeToken + generics กับ Gson ใน Kotlin


111

ฉันไม่สามารถรับรายการประเภททั่วไปจากคลาสที่กำหนดเองได้ (เทิร์น):

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

มันกล่าวว่า:

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

คำตอบ:


243

สร้างความสนุกแบบอินไลน์:

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

จากนั้นคุณสามารถเรียกมันด้วยวิธีนี้:

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

ทางเลือกก่อนหน้า:

ทางเลือก 1:

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

คุณต้องใส่object :และประเภทเฉพาะในfromJson<List<Turns>>


ทางเลือก 2:

ดังที่ @cypressious กล่าวถึงก็สามารถทำได้ด้วยวิธีนี้:

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

ใช้เป็น:

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

4
คุณยังสามารถสร้างวิธีการช่วยเหลือที่ทำเพื่อคุณ:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Kirill Rakhman

1
หรือแม้กระทั่งขยาย Gson ให้มีโอเวอร์โหลดใหม่จาก Json ที่ทำสิ่งนี้ Kotlin ถูกออกแบบมาเพื่อขยายดังนั้นให้ขยาย Gson เพื่อให้ดีขึ้นและซ่อน TypeTokens
Jayson Minard

ฉันทำการแก้ไขที่แนะนำซึ่งทำให้คำตอบสมบูรณ์และเป็นทางการมากขึ้นเนื่องจากหลายคนที่ใช้ Gson น่าจะเห็นคำตอบนี้ ฉันเพิ่มคำอธิบายในคำตอบและลิงก์ไปยังการอ้างอิง Kotlin สำหรับหัวข้อที่ใช้ในการแก้ปัญหา ... ดังนั้นผู้คนไม่จำเป็นต้องอ่านคำตอบหรือความคิดเห็นอื่น ๆ ทั้งหมดที่เข้ามาในนี้ หากคุณยอมรับการแก้ไขฉันสามารถลบคำตอบของฉันด้านล่าง
Jayson Minard

แก้ไขถูกปฏิเสธดูคำตอบของฉันด้านล่างสำหรับเวอร์ชันเต็มที่รวมคำตอบและความคิดเห็นทั้งหมดไว้ในคำตอบเดียว คุณยอมรับคำตอบของคุณเอง แต่ยังไม่สมบูรณ์
Jayson Minard

ลบคำเตือน kotlin: inline fun <reified T> genericType (): พิมพ์? = object: TypeToken <T> () {} .type
Juan Mendez

29

วิธีนี้ช่วยแก้ปัญหาได้:

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

บรรทัดแรกสร้างนิพจน์อ็อบเจ็กต์ที่ต่อจากTypeTokenนั้นรับ Java Typeจากนั้น จากนั้นGson().fromJsonเมธอดต้องการชนิดที่ระบุสำหรับผลลัพธ์ของฟังก์ชัน (ซึ่งควรตรงกับที่TypeTokenสร้างขึ้น) สองเวอร์ชันของงานนี้ตามด้านบนหรือ:

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

เพื่อให้ง่ายต่อการสร้างTypeTokenคุณสามารถสร้างฟังก์ชันตัวช่วยซึ่งจำเป็นต้องอยู่ในบรรทัดเพื่อให้สามารถใช้พารามิเตอร์ประเภท reified :

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

ซึ่งสามารถใช้ได้ด้วยวิธีใดวิธีหนึ่งต่อไปนี้:

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

และกระบวนการทั้งหมดสามารถรวมเป็นฟังก์ชันส่วนขยายสำหรับGsonอินสแตนซ์:

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

เพื่อให้คุณสามารถโทรหา Gson ได้และไม่ต้องกังวลTypeTokenเลย:

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

ที่นี่ Kotlin ใช้การอนุมานประเภทจากด้านหนึ่งของการมอบหมายหรืออีกด้านหนึ่งและ reified generics สำหรับฟังก์ชันอินไลน์เพื่อส่งผ่านประเภทเต็ม (โดยไม่ต้องลบ) และใช้สิ่งนั้นเพื่อสร้าง a TypeTokenและโทรไปยัง Gson


1
สวัสดี @Jayson ฉันไม่สามารถทำให้มันสนุกแบบนี้ใน Android Studio ได้ ดูเหมือนจะโอเค แต่ไม่เป็นที่รู้จักเมื่อฉันทำGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia

@juancho ช่วยบอกหน่อยได้ไหมว่า "ไม่รู้จัก" หมายความว่าอย่างไร ข้อผิดพลาดของคอมไพเลอร์? คุณมีวิธีการนำเข้าและใช้ได้จากด้านบนหรือไม่
Jayson Minard

2
ฉันคัดลอกและวางโค้ดของคุณใน Android Studio และนำเข้าความสนุกในคลาส kotlin ของฉัน ฉันพยายามทำตามที่คุณพูด แต่ด้วยเหตุผลบางอย่างคอมไพเลอร์บอกฉันว่าความสนุกนี้ไม่มีอยู่จริง ฉันใช้ฟังก์ชันการขยายอื่น ๆ อยู่แล้ว แต่ฉันไม่รู้ว่าข้อเสนอแนะของคุณใช้ไม่ได้ผล คุณใช้ AS และ Kotlin เวอร์ชันใด เพื่อลองอีกครั้ง
Juan Saravia

สิ่งนี้ไม่เกี่ยวข้องกับสตูดิโอ Android โดยตรง Kotlin เหมือนกันทั้งภายในและภายนอก คุณกำลังสร้างอินสแตนซ์Gson()หรือGsonเหมือนกับว่ามันเป็นแบบคงที่? คุณต้องมีอินสแตนซ์แรก
Jayson Minard

22

อีกทางเลือกหนึ่ง (ไม่แน่ใจว่ามันดูหรูหรากว่าตัวอื่น) อาจเป็นการเรียกแบบนี้:

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

ดังนั้นคุณจึงใช้ซับ java Array class one แทน "pure Kotlin"


2
เนื่องจาก TypeToken ไม่สามารถใช้งานได้กับโทรศัพท์ทุกเครื่องที่เชื่อถือได้นี่จึงเป็นทางออกที่ดีที่สุดสำหรับฉัน ซับในง่ายด้วยโคตลินบริสุทธิ์
LuckyMalaka

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

เป็นวิธีของฉันในการแยกวิเคราะห์อาร์เรย์ข้อมูลใน kotlin


นี่ควรเป็นคำตอบที่ยอมรับสั้นและเรียบง่าย
Umar Ata

6

ผมใช้บางอย่างเช่นนี้การแปลงTไปstringและStringกลับไปใช้T Gsonไม่ใช่สิ่งที่คุณกำลังมองหา แต่ในกรณี

กำลังประกาศนามสกุล

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)

การใช้งาน

// 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

วิธีนี้ใช้งานได้ดีและง่ายกว่า

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

ประเภทการส่งคืนต้องเป็นโมฆะ มิฉะนั้นโค้ด Java ในไลบรารี Gson สามารถส่งคืนค่า null ได้ แต่ Kotlin ถือว่าชนิดไม่เป็นค่าว่าง เป็นผลให้คุณได้รับ NPE ใน Kotlin
fdermishin

1

Kotlin generic reified functionof Gson deserialize เพื่อArrayList<T>ใช้รหัสนี้

 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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.