好的,我目前正在处理的 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())
}
}
一切都解决之后,我想将数据存储在本地领域数据库中,以防我失去互联网连接或类似的情况,但我目前陷入这个问题,无法让它工作。
总的来说,我对编程还算陌生,这对我来说只是一个“小”学习项目,但即使在谷歌搜索并尝试了无数的事情之后,我也无法让它工作,所以任何帮助都是值得赞赏的。
尝试去掉“/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}")
}