Home > OS >  Retrofit 2.7.2 GSON error when using a retrofit call, the error is "Unable to invoke no-args co
Retrofit 2.7.2 GSON error when using a retrofit call, the error is "Unable to invoke no-args co

Time:04-07

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
)
  • Related