I am using android studio and kotlin attempting to make an API call using retrofit with gson library. I used the JSON to kotlin data class plugin to make my data classes. I have looked for solutions and havent been able to find anything that exactly matches my issue here. I looked up how to add a no args constructor in kotlin but that involves setting all the fields to default values which is not what I want. I wondered if somehow it was calling the API but not receiving anything in the conversion rates but I wasn't exactly sure how to check since I instantly get thrown the error when calling the API.
The exact error I am receiving is:
"Unable to invoke no-args constructor for class com.schrader.currency_take_two.data.models.ConversionRates. Registering an InstanceCreator with Gson for this type may fix this problem."
Here is my build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'dagger.hilt.android.plugin'
id 'kotlin-kapt'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.schrader.currency_take_two"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures{
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// dagger-hilt
implementation 'com.google.dagger:hilt-android:2.41'
kapt 'com.google.dagger:hilt-compiler:2.41'
// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
implementation 'com.squareup.okhttp3:okhttp:3.6.0'
// coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
// ViewModel
def lifecycle_version = "2.4.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.core:core-ktx:1.7.0"
//fragment KTX library
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation "androidx.activity:activity-ktx:1.4.0"
// Room DB
def room_version = "2.4.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
Here are the data classes one is shortened here because it is very long:
package com.schrader.currency_take_two.data.models
import com.google.gson.annotations.SerializedName
data class CurrencyResponse(
@SerializedName("base_code")
val baseCode: String,
@SerializedName("conversion_rates")
val conversionRates: ConversionRates,
@SerializedName("documentation")
val documentation: String,
@SerializedName("result")
val result: String,
@SerializedName("terms_of_use")
val termsOfUse: String,
@SerializedName("time_last_update_unix")
val timeLastUpdateUnix: Int,
@SerializedName("time_last_update_utc")
val timeLastUpdateUtc: String,
@SerializedName("time_next_update_unix")
val timeNextUpdateUnix: Int,
@SerializedName("time_next_update_utc")
val timeNextUpdateUtc: String
)
package com.schrader.currency_take_two.data.models
import com.google.gson.annotations.SerializedName
data class ConversionRates(
@SerializedName("AED")
val aED: Double,
@SerializedName("AFN")
val aFN: Double,
@SerializedName("ALL")
val aLL: Double,
@SerializedName("AMD")
val aMD: Double,
@SerializedName("ANG")
val aNG: Double,
@SerializedName("AOA")
val aOA: Double,
@SerializedName("ARS")
val aRS: Double
)
Here is the retrofit call and my retrofit instance:
interface CurrencyApi {
@GET("/v6/API_KEY/latest/{Currency}")
suspend fun getRates(
@Path("Currency") currency: String //This adds onto the API URL in the {} in the GET, @Query adds a query to the URL 'Query={Query}'
): Response<CurrencyResponse>
}
The API call doesnt make it to the first Log call and instantly errors out to the catch block.
class Default_API_Repository @Inject constructor(
private val api: CurrencyApi
) : API_Repository {
override suspend fun getRates(currency: String): Resource<CurrencyResponse> {
return try{
val response = api.getRates(currency)
Log.d("API", "${response.toString()}")
val result = response.body()
if(response.isSuccessful && response != null ){
Resource.Success(result)
}else{
Log.d("API", "${response.message()}")
Resource.Error(response.message())
}
}catch (e: Exception){
Log.d("API", "${e.message}")
Resource.Error(e.message ?: "An error occurred")
}
}
}
private const val BASE_URL = "https://v6.exchangerate-api.com"
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideCurrencyApi(): CurrencyApi = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(CurrencyApi::class.java)
This is how I call the API in the main activity:
GlobalScope.launch(Dispatchers.IO){
var response = apiRepo.getRates("USD")
if(response.data == null){
Log.d("MAIN", "${response.message}")
}
Log.d("MAIN", "${response.toString()}")
}
Here is the exact JSON from the API:
{
"result":"success",
"documentation":"https://www.exchangerate-api.com/docs",
"terms_of_use":"https://www.exchangerate-api.com/terms",
"time_last_update_unix":1649203202,
"time_last_update_utc":"Wed, 06 Apr 2022 00:00:02 0000",
"time_next_update_unix":1649289602,
"time_next_update_utc":"Thu, 07 Apr 2022 00:00:02 0000",
"base_code":"USD",
"conversion_rates":{
"USD":1,
"AED":3.6725,
"AFN":88.6534,
"ALL":110.0838,
"AMD":480.1007,
"ANG":1.7900,
"AOA":445.0124,
"ARS":111.4453,
"AUD":1.3155,
"AWG":1.7900,
"AZN":1.6961,
"BAM":1.7892,
"BBD":2.0000,
"BDT":85.2904,
"BGN":1.7884,
"BHD":0.3760,
"BIF":2005.2598,
"BMD":1.0000,
"BND":1.3581,
"BOB":6.8635,
"BRL":4.6151,
"BSD":1.0000,
"BTN":75.3175,
"BWP":11.5278,
"BYN":2.8692,
"BZD":2.0000,
"CAD":1.2468,
"CDF":1995.8399,
"CHF":0.9283,
"CLP":779.3730,
"CNY":6.3692,
"COP":3716.7222,
"CRC":660.0122,
"CUP":24.0000,
"CVE":100.8701,
"CZK":22.2978,
"DJF":177.7210,
"DKK":6.8247,
"DOP":54.9168,
"DZD":143.1850,
"EGP":18.2463,
"ERN":15.0000,
"ETB":51.2293,
"EUR":0.9148,
"FJD":2.0753,
"FKP":0.7634,
"FOK":6.8247,
"GBP":0.7634,
"GEL":3.0853,
"GGP":0.7634,
"GHS":7.7634,
"GIP":0.7634,
"GMD":54.8016,
"GNF":8946.2447,
"GTQ":7.6691,
"GYD":209.6041,
"HKD":7.8373,
"HNL":24.5487,
"HRK":6.8925,
"HTG":107.4322,
"HUF":341.7488,
"IDR":14374.8444,
"ILS":3.2117,
"IMP":0.7634,
"INR":75.3200,
"IQD":1462.3970,
"IRR":41938.4058,
"ISK":129.1085,
"JEP":0.7634,
"JMD":153.5796,
"JOD":0.7090,
"JPY":123.3508,
"KES":115.6196,
"KGS":85.8864,
"KHR":4057.0179,
"KID":1.3156,
"KMF":450.0508,
"KRW":1215.3051,
"KWD":0.2996,
"KYD":0.8333,
"KZT":466.7579,
"LAK":13220.0876,
"LBP":1507.5000,
"LKR":295.1163,
"LRD":152.7083,
"LSL":14.6202,
"LYD":4.6812,
"MAD":9.4783,
"MDL":18.3389,
"MGA":3035.6361,
"MKD":56.0557,
"MMK":1825.6103,
"MNT":2954.7230,
"MOP":8.0723,
"MRU":36.4708,
"MUR":44.3148,
"MVR":15.4209,
"MWK":820.4555,
"MXN":19.9358,
"MYR":4.2051,
"MZN":64.2611,
"NAD":14.6202,
"NGN":415.4445,
"NIO":35.8669,
"NOK":8.7398,
"NPR":120.5080,
"NZD":1.4357,
"OMR":0.3845,
"PAB":1.0000,
"PEN":3.6681,
"PGK":3.5228,
"PHP":51.2574,
"PKR":183.8781,
"PLN":4.2286,
"PYG":7064.7743,
"QAR":3.6400,
"RON":4.5173,
"RSD":107.6495,
"RUB":83.3695,
"RWF":1058.3464,
"SAR":3.7500,
"SBD":7.8290,
"SCR":14.3972,
"SDG":445.3390,
"SEK":9.4126,
"SGD":1.3582,
"SHP":0.7634,
"SLL":11827.2387,
"SOS":579.5740,
"SRD":20.7475,
"SSP":425.3169,
"STN":22.4125,
"SYP":2511.1358,
"SZL":14.6202,
"THB":33.5163,
"TJS":12.5014,
"TMT":3.5000,
"TND":2.7588,
"TOP":2.2203,
"TRY":14.7205,
"TTD":6.7529,
"TVD":1.3156,
"TWD":28.6146,
"TZS":2322.3918,
"UAH":29.1999,
"UGX":3547.1133,
"UYU":41.2934,
"UZS":11432.7976,
"VES":4.4095,
"VND":22865.8925,
"VUV":110.6141,
"WST":2.5488,
"XAF":600.0677,
"XCD":2.7000,
"XDR":0.7277,
"XOF":600.0677,
"XPF":109.1646,
"YER":250.3338,
"ZAR":14.6364,
"ZMW":17.6268,
"ZWL":142.3748
}
}
CodePudding user response:
You need to initialize them. One of the reason maybe the value is null
.
data class CurrencyResponse(
@SerializedName("base_code")
val baseCode: String? = null,
@SerializedName("conversion_rates")
val conversionRates: ConversionRates? = null,
@SerializedName("documentation")
val documentation: String? = null,
@SerializedName("result")
val result: String? = null,
@SerializedName("terms_of_use")
val termsOfUse: String? = null,
@SerializedName("time_last_update_unix")
val timeLastUpdateUnix: Int? = null,
@SerializedName("time_last_update_utc")
val timeLastUpdateUtc: String? = null,
@SerializedName("time_next_update_unix")
val timeNextUpdateUnix: Int? = null,
@SerializedName("time_next_update_utc")
val timeNextUpdateUtc: String? = null
)