Cách sử dụng TypeToken + generics với Gson trong Kotlin


108

Tôi không thể nhận Danh sách loại chung từ một lớp tùy chỉnh (Lượt):

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

nó nói rằng:

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

Câu trả lời:


236

Tạo niềm vui nội tuyến này:

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

và sau đó bạn có thể gọi nó theo cách này:

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

Các lựa chọn thay thế trước đó:

ĐẠI SỐ 1:

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

Bạn phải đặt object :và loại cụ thể trongfromJson<List<Turns>>


ĐẠI SỐ 2:

Như @cypressious đã đề cập, nó cũng có thể đạt được theo cách này:

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

sử dụng như là:

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

4
Bạn cũng có thể tạo ra một phương pháp helper nào đó dành cho bạn:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Kirill Rakhman

1
hoặc thậm chí mở rộng Gson để có quá tải fromJson mới thực hiện điều này. Kotlin được thiết kế để mở rộng, vì vậy hãy mở rộng Gson để làm cho nó đẹp hơn và ẩn TypeTokens.
Jayson Minard

Tôi đã thực hiện một chỉnh sửa được đề xuất để làm cho câu trả lời hoàn chỉnh và trang trọng hơn vì câu trả lời này có thể được nhiều người sử dụng Gson nhìn thấy. Tôi đã thêm giải thích trong câu trả lời và liên kết đến tài liệu tham khảo Kotlin cho các chủ đề được sử dụng để giải quyết vấn đề ... vì vậy mọi người không phải đọc tất cả các câu trả lời hoặc nhận xét khác liên quan đến vấn đề này. Nếu bạn chấp nhận chỉnh sửa, tôi có thể xóa câu trả lời của mình bên dưới.
Jayson Minard

Chỉnh sửa bị từ chối, hãy xem câu trả lời của tôi bên dưới để có phiên bản đầy đủ kết hợp tất cả các câu trả lời và nhận xét thành một câu trả lời mạch lạc. Bạn đã chấp nhận câu trả lời của riêng mình nhưng nó chưa hoàn chỉnh.
Jayson Minard

loại bỏ cảnh báo kotlin: inline fun <reified T> genericType (): Loại? = object: TypeToken <T> () {} .type
Juan Mendez

28

Điều này giải quyết vấn đề:

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

Dòng đầu tiên tạo một biểu thức đối tượng giảm dần TypeTokenvà sau đó lấy Java Typetừ đó. Sau đó, Gson().fromJsonphương thức cần kiểu được chỉ định cho kết quả của hàm (phải khớp với kiểu TypeTokenđược tạo). Hai phiên bản của tác phẩm này, như trên hoặc:

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

Để tạo dễ dàng hơn, TypeTokenbạn có thể tạo một hàm trợ giúp, hàm này được yêu cầu phải nội tuyến để có thể sử dụng các tham số kiểu đã sửa đổi :

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

Sau đó, có thể được sử dụng theo một trong hai cách sau:

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

Và toàn bộ quá trình có thể được bao bọc thành một hàm mở rộng cho Gsonví dụ:

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

Vì vậy, bạn có thể chỉ cần gọi cho Gson và không phải lo lắng về TypeToken:

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

Ở đây Kotlin đang sử dụng kiểu suy luận từ một phía của nhiệm vụ hoặc mặt khác, và các tổng thể được sửa đổi cho một hàm nội tuyến để chuyển qua kiểu đầy đủ (không tẩy xóa) và sử dụng kiểu đó để tạo một TypeTokenvà cũng thực hiện lệnh gọi tới Gson


1
Xin chào @Jayson, tôi không thể làm cho nó hoạt động thú vị như thế này trong Android Studio. Có vẻ là OK nhưng nó không được ghi nhận khi tôi làmGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia

@juancho bạn có thể cho tôi biết "không nhận dạng được" nghĩa là gì không? Một lỗi trình biên dịch? Bạn đã nhập và có sẵn phương thức mở rộng từ bên trên?
Jayson Minard

2
Tôi sao chép và dán mã của bạn trong Android Studio và nhập niềm vui vào lớp kotlin của tôi. Tôi đã cố gắng làm những gì bạn nói nhưng vì lý do nào đó mà trình biên dịch nói với tôi rằng niềm vui này không tồn tại. Tôi đã sử dụng các hàm mở rộng khác nhưng tôi không biết gợi ý của bạn không hoạt động. Bạn đang sử dụng phiên bản AS và Kotlin nào? chỉ để thử lại.
Juan Saravia

Điều này không liên quan trực tiếp đến Android studio, Kotlin bên trong hay bên ngoài đều giống nhau. Bạn có đang tạo phiên bản của Gson()hoặc chỉ Gsonnhư thể nó là tĩnh? Bạn cần cái đầu tiên, một ví dụ.
Jayson Minard

21

Một tùy chọn khác (không chắc nó trông thanh lịch hơn những cái khác) có thể là một cuộc gọi như thế này:

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

Vì vậy, bạn đang sử dụng lớp java Array lớp một thay vì "Kotlin thuần túy".


2
Vì TypeToken không hoạt động trên mọi điện thoại đáng tin cậy, đây là giải pháp tốt nhất cho tôi. Một lớp lót dễ dàng với kotlin nguyên chất.
LuckyMalaka

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

Đó là cách của tôi để phân tích cú pháp mảng dữ liệu trong kotlin.


Đây phải là câu trả lời được chấp nhận, ngắn gọn và đơn giản.
Umar Ata

6

Tôi đã sử dụng một cái gì đó như thế này để chuyển đổi Tsang string& Stringquay lại Tsử dụng Gson. Không chính xác những gì bạn đang tìm kiếm nhưng chỉ trong trường hợp.

Khai báo phần mở rộng

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)

Sử dụng

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

Điều này cũng hoạt động và đơn giản hơn

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

Kiểu trả về phải là nullable. Nếu không, mã Java trong thư viện Gson có thể trả về null, nhưng Kotlin giả định rằng kiểu này là không thể null. Kết quả là bạn nhận được NPE trong Kotlin.
fdermishin

1

Kotlin generic reified functioncủa Gson deserialize để ArrayList<T>sử dụng mã này

 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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.