Home > Net >  Parse JSON list to object Kotlin Retrofit and Moshi
Parse JSON list to object Kotlin Retrofit and Moshi

Time:07-27

Hi I'm use Kotlin retrofit and moshi I have that JSON

[
{
    "id": "bitcoin",
    "symbol": "btc",
    "name": "Bitcoin",
    "image": "https://assets.coingecko.com/coins/images/1/large/bitcoin.png?1547033579",
    "current_price": 21917,
    "market_cap": 418101666154,
    "market_cap_rank": 1,
    "fully_diluted_valuation": 459616529150,
    "total_volume": 35275748678,
    "high_24h": 22967,
    "low_24h": 21800,
    "price_change_24h": -785.2614865719079,
    "price_change_percentage_24h": -3.4589,
    "market_cap_change_24h": -16487597586.019958,
    "market_cap_change_percentage_24h": -3.79383,
    "circulating_supply": 19103175,
    "total_supply": 21000000,
    "max_supply": 21000000,
    "ath": 69045,
    "ath_change_percentage": -68.28207,
    "ath_date": "2021-11-10T14:24:11.849Z",
    "atl": 67.81,
    "atl_change_percentage": 32195.96528,
    "atl_date": "2013-07-06T00:00:00.000Z",
    "roi": null,
    "last_updated": "2022-07-25T18:38:56.019Z"
  },
  {
    "id": "ethereum",
    "symbol": "eth",
    "name": "Ethereum",
    "image": "https://assets.coingecko.com/coins/images/279/large/ethereum.png?1595348880",
    "current_price": 1522.52,
    "market_cap": 181649214301,
    "market_cap_rank": 2,
    "fully_diluted_valuation": null,
    "total_volume": 17695232530,
    "high_24h": 1651.4,
    "low_24h": 1510.24,
    "price_change_24h": -72.84932292922076,
    "price_change_percentage_24h": -4.56629,
    "market_cap_change_24h": -10281152619.016693,
    "market_cap_change_percentage_24h": -5.35671,
    "circulating_supply": 119829051.14353,
    "total_supply": 119829051.14353,
    "max_supply": null,
    "ath": 4878.26,
    "ath_change_percentage": -68.9122,
    "ath_date": "2021-11-10T14:24:19.604Z",
    "atl": 0.432979,
    "atl_change_percentage": 350158.16453,
    "atl_date": "2015-10-20T00:00:00.000Z",
    "roi": {
      "times": 91.78338127800683,
      "currency": "btc",
      "percentage": 9178.338127800684
    },
    "last_updated": "2022-07-25T18:39:03.483Z"
  },
  {
    "id": "tether",
    "symbol": "usdt",
    "name": "Tether",
    "image": "https://assets.coingecko.com/coins/images/325/large/Tether-logo.png?1598003707",
    "current_price": 1.001,
    "market_cap": 65934603930,
    "market_cap_rank": 3,
    "fully_diluted_valuation": null,
    "total_volume": 49334676401,
    "high_24h": 1.011,
    "low_24h": 0.996453,
    "price_change_24h": 0.00015041,
    "price_change_percentage_24h": 0.01503,
    "market_cap_change_24h": 33333966,
    "market_cap_change_percentage_24h": 0.05058,
    "circulating_supply": 65876317670.4762,
    "total_supply": 65876317670.4762,
    "max_supply": null,
    "ath": 1.32,
    "ath_change_percentage": -24.34669,
    "ath_date": "2018-07-24T00:00:00.000Z",
    "atl": 0.572521,
    "atl_change_percentage": 74.83471,
    "atl_date": "2015-03-02T00:00:00.000Z",
    "roi": null,
    "last_updated": "2022-07-25T18:36:04.706Z"
  }
]

So I make a 2 classes

data class TopCrypto(
    val cryptos: List<CryptoStock>? = null
    )

and

data class CryptoStock(
    val id: String? = null,
    val symbol: String? = null,
    val image: String? = null,
    val currentPrice: String? = null,
    val priceChange: Double? = null
)

i try use Call<List> but it's not what i want. I'd like to have respons in TopCrypto

interface CryptoService {
    @GET("coins/markets")
    fun getTopCrypto(@Query("vs_currency") vsCurrency:String,
                     @Query("per_page") limit: Int,
                     @Query("order") order:String = "market_cap_desc"
    ): Call<TopCrypto>

}

object

object Api {
    private val BASE_URL = "https://api.coingecko.com/api/v3/"

    private val moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

    private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .baseUrl(BASE_URL)
        .build()

    val retrofitService: CryptoService by lazy{
        retrofit.create(CryptoService::class.java)
    }

}
class CryptoManager {

    init {
        getCryptos()
    }

    private val _cryptoResponse = mutableStateOf(TopCrypto())
    val cryptoResponse: State<TopCrypto>
        @Composable get() = remember {
            _cryptoResponse
        }


    private fun getCryptos(){
        val service = Api.retrofitService.getTopCrypto("usd",10)
        service.enqueue(object : Callback<TopCrypto> {
            override fun onResponse(call: Call<TopCrypto>, response: Response<TopCrypto>) {
                if (response.isSuccessful){
                    _cryptoResponse.value = response.body()!!
                    Log.d("crypto","${_cryptoResponse.value}")
                }else{
                    Log.d("error","${response.errorBody()}")
                }
            }

            override fun onFailure(call: Call<TopCrypto>, t: Throwable) {
                Log.d("error","${t.message}")
            }

        })
    }
}

For now i have Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $. I know i must map somehow Json array to my class TopCrypto Thanks in advance for help :)

CodePudding user response:

I do like Eric and Jorn said :). Remove class TopCrypto and change

CryptoManager class

class CryptoManager {

    init {
        getCryptos()
    }

    private val _cryptoResponse = mutableStateOf(CryptoStock())
    val cryptoResponse: MutableState<CryptoStock>
        @Composable get() = remember {
            _cryptoResponse
        }


    private fun getCryptos(){


        val service = Api.retrofitService.getTopCrypto("usd",5)


            service.enqueue(object : Callback<List<CryptoStock>>
            {
                 override fun onResponse(call: Call<List<CryptoStock>>, response: Response<List<CryptoStock>>) {
                     if (response.isSuccessful){
                         Log.d("apiHeaders","${response.headers()}")
                         Log.d("apiBody","${response.body()}")
                     }else{
                         Log.d("error","${response.errorBody()}")
                     }
                 }

                override fun onFailure(call: Call<List<CryptoStock>>, t: Throwable) {
                    Log.e("TAG", "login() - onFailure() ", t)
                }

             })

}

and inteface

interface CryptoService {
    @GET("coins/markets")
    fun getTopCrypto(@Query("vs_currency") vsCurrency:String,
                     @Query("per_page") limit: Int,
                     @Query("order") order:String = "market_cap_desc"
    ): Call<List<CryptoStock>>

}

CodePudding user response:

Problem is in creating data class from JSON string. Your data class TopCrypto requires object but response json string has list so I have tried to modify data class, Can you please try below data class once

class TopCrypto : ArrayList<TopCrypto.TopCryptoData>(){
    data class TopCryptoData(
        val ath: Double,
        val ath_change_percentage: Double,
        val ath_date: String,
        val atl: Double,
        val atl_change_percentage: Double,
        val atl_date: String,
        val circulating_supply: Double,
        val current_price: Double,
        val fully_diluted_valuation: Long,
        val high_24h: Double,
        val id: String,
        val image: String,
        val last_updated: String,
        val low_24h: Double,
        val market_cap: Long,
        val market_cap_change_24h: Double,
        val market_cap_change_percentage_24h: Double,
        val market_cap_rank: Int,
        val max_supply: Int,
        val name: String,
        val price_change_24h: Double,
        val price_change_percentage_24h: Double,
        val roi: Roi,
        val symbol: String,
        val total_supply: Double,
        val total_volume: Long
    ) {
        data class Roi(
            val currency: String,
            val percentage: Double,
            val times: Double
        )
    }
}
  • Related