将 Retrofit Json 转换为 Kotlin 映射

问题描述 投票:0回答:1

好的,我目前正在处理的 Kotlin 多平台项目遇到问题。

我想从 api (http://www.floatrates.com/daily/usd.json) 获取一些货币兑换率并将其保存在本地数据库(领域)中。

我目前面临的问题是,JSON 中的所有数据都保存在许多唯一命名的对象中,而不是保存在数组中(这些对象都具有相同的结构)。

{
"aud": {
    "code": "AUD",
    "alphaCode": "AUD",
    "numericCode": "036",
    "name": "Australian Dollar",
    "rate": 1.5120512400088,
    "date": "Wed, 13 Mar 2024 12:55:07 GMT",
    "inverseRate": 0.66135324884504
},
"cad": {
    "code": "CAD",
    "alphaCode": "CAD",
    "numericCode": "124",
    "name": "Canadian Dollar",
    "rate": 1.3495144747695,
    "date": "Wed, 13 Mar 2024 12:55:07 GMT",
    "inverseRate": 0.74100724274989
},
.
.
 // hundreds more
.
.
"inr": {
    "code": "INR",
    "alphaCode": "INR",
    "numericCode": "356",
    "name": "Indian Rupee",
    "rate": 82.850734958333,
    "date": "Wed, 13 Mar 2024 12:55:07 GMT",
    "inverseRate": 0.012069898963515
},
"jpy": {
    "code": "JPY",
    "alphaCode": "JPY",
    "numericCode": "392",
    "name": "Japanese Yen",
    "rate": 147.78881616883,
    "date": "Wed, 13 Mar 2024 12:55:07 GMT",
    "inverseRate": 0.0067664118701486
}
}

我想将这些货币保存在一些可迭代的数据结构中(例如

Map<String,Currency>
):

data class Currency(
   val code: String,
   val alphaCode: String,
   val numericCode: String,
   val name: String,
   val rate: Double,
   val date: String,
   val inverseRate: Double,
 )

但我无法使用 Retrofit 将数据存储到我的地图中。

如果我使用 android studio 的 Json to kotlin class 插件,我可以生成这样的类:

data class AutoGeneratedResponseDataClass(
   val aed: Aed,
   val afn: Afn,
   val all: All,
   val amd: Amd,
   .
   .
    // hundreds more
   .
   .
   val xpf: Xpf,
   val yer: Yer,
   val zar: Zar,
   val zmw: Zmw,
)

有数百个类似的其他类都完全相同

 data class Aed(
    val alphaCode: String,
    val code: String,
    val date: String,
    val inverseRate: Double,
    val name: String,
    val numericCode: String,
    val rate: Double
)
.
.
.
data class Zmw(
    val alphaCode: String,
    val code: String,
    val date: String,
    val inverseRate: Double,
    val name: String,
    val numericCode: String,
    val rate: Double
)

我认为从技术上讲,可以通过使用反射来迭代对象的所有字段,但是我可以将所有这些类类型更改为一个类,但这仍然不是我真正想要的。

为了测试一切是否正常工作,我有一个对话框,它以字符串和两个按钮的形式显示 json,一个用于正常工作的测试 api,一个用于此 api,无论我尝试什么,都只返回“null”

Button(
onClick = { viewModel.getCurrencies() }
) {
    Text(text = "sync currencies")
}
Button(
onClick = { viewModel.getQuotes() }
) {
    Text(text = "sync quotes")
}
Button(
onClick = { viewModel.showDialog = true }
) {
    Text(text = "Show Response")
}

测试API的代码是:

//This code works and gets me the desired output
object RetrofitHelperQuotes {
    val baseUrl = "https://quotable.io/"

    fun getInstance(): Retrofit {
        return Retrofit.Builder().baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}



interface QuotesApi {
    @GET("/quotes")
    suspend fun getQuotes() : Response<QuoteList>
}
//with 
data class QuoteList(
    val count: Int,
    val lastItemIndex: Int,
    val page: Int,
    val results: List<Result>,
    val totalCount: Int,
    val totalPages: Int
)

data class Result(
    val _id: String,
    val author: String,
    val authorSlug: String,
    val content: String,
    val dateAdded: String,
    val dateModified: String,
    val length: Int,
    val tags: List<String>
)

// and this code in the viewmodel that gets called on button press
@OptIn(DelicateCoroutinesApi::class)
fun getQuotes(){
    val quotesApi = RetrofitHelperQuotes.getInstance().create(QuotesApi::class.java)
    GlobalScope.launch {
        val result = quotesApi.getQuotes()
        if (result != null)
//this is the string that gets stored in the viewModel and gets displayed onscreen when i press the third button
            jsonResponse = GsonBuilder().setPrettyPrinting().create().toJson(result.body())
    }
}

我实际想要使用的API的代码是:

object RetrofitHelperCurrency {
    val baseUrl = "http://www.floatrates.com/daily/"

    fun getInstance(): Retrofit {
        return Retrofit.Builder().baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

interface CurrencyApi {

    @GET("/usd.json")
    suspend fun getCurrencies() : Response<AutoGeneratedResponseDataClass>

    @GET("/usd.json")
    suspend fun anotherTry() : Response<Map<String,Currency>>

    @GET("/usd.json")
    suspend fun yetAnotherTry() : Response<CurrencyApiResponse>
}
//with
data class CurrencyApiResponse(
    val data: Map<String,Currency>,
)
// and the rest of my classes as described at the top

// and this code in the viewmodel that gets called on button press
@OptIn(DelicateCoroutinesApi::class)
fun getCurrencies(){
    val currencyApi = RetrofitHelperCurrency.getInstance().create(CurrencyApi::class.java)
    GlobalScope.launch {
        val result = currencyApi.getCurrencies()
        if (result != null)
//this is the string that gets stored in the viewModel and gets displayed onscreen when i press the third button
            jsonResponse = GsonBuilder().setPrettyPrinting().create().toJson(result.body())

    }
}

一切都解决之后,我想将数据存储在本地领域数据库中,以防我失去互联网连接或类似的情况,但我目前陷入这个问题,无法让它工作。

总的来说,我对编程还算陌生,这对我来说只是一个“小”学习项目,但即使在谷歌搜索并尝试了无数的事情之后,我也无法让它工作,所以任何帮助都是值得赞赏的。

kotlin gson retrofit2 kotlin-multiplatform
1个回答
0
投票

尝试去掉“/usd.json”前面的“/”。

使用“/usd.json”会生成以下 URI:“http://www.floatrates.com/usd.json”。但正确的是“http://www.floatrates.com/daily/usd.json”。用wireshark测试过。

以下内容帮助我将结果提取到货币列表中:

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET

interface CurrencyService {
    @GET("usd.json")
    fun getUsd(): Call<Map<String, Currency?>?>?
}

data class Currency(
    val code: String,
    val alphaCode: String,
    val numericCode: String,
    val name: String,
    val rate: Double,
    val date: String,
    val inverseRate: Double
)

fun main() {
    val retrofit = Retrofit.Builder()
        .baseUrl("http://www.floatrates.com/daily/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val service = retrofit.create(CurrencyService::class.java)

    val currencyMap = service.getUsd()?.execute()?.body()
    val currencies = currencyMap?.values
    println("repos: ${currencies}")
}
© www.soinside.com 2019 - 2024. All rights reserved.