Parse Json response from CoinGecko API with dynamic variable - android

I am trying to read the response from https://api.coingecko.com/api/v3/simple/price (for example with the fields: ids=bitcoin) where the "ids" field can be any cryptocurrency.
In order to read effectively the response i must have a data class like this one:
data class PortfolioCoinsDTO(
val bitcoin : PortfolioCoinItem
)
with an extra class:
data class PortfolioCoinItem(
val usd: Double,
val usd_market_cap: Double,
val usd_24h_change: Double
)
The above code works only when declaring the cryptocurrency as a variable in the DTO beforehand. Is there any way to solve this? So for example you can map the variable of the DTO data class to any cryptocurrency? (i am trying to see if with deserialization there might be a soluton).

Related

Android moshi generic type with different types

I am using retrofit with moshi converter in my android (kotlin) app.
The api response have a general format, where I wrote a class with generic field as the following:
`
#JsonClass(generateAdapter = true)
data class ApiResponse<T>(
#Json(name = "Status")
val status: Boolean,
#Json(name = "Message")
val message: String?,
#Json(name = "Data")
val data: T?
)
`
data field can be of any type. It is working fine in a normal cases. But sometimes the data field in api response comes with different types for the same api.
For example: in login api I have two cases:
Successful login:
{"Status":true, "Message":"Successful Login", "Data":{"userID":"user id", "userName":"user name"}}
Failed login:
{"Status":false, "Message":"Login Failed", "Data":false}
In first case, data is an object, while in second case data is a boolean. So my converter throws an error
I tried to write a custom adapter, to set the data field to null in case it was a boolean in the json, but I failed to handle such generic case, I had to write a custom adapter for every response type.
Is there are anyway to handle this issue without modifying the api code?
Thanks in advance.

Moshi - Not enough information to infer type variable T

I have an app which is mixed Java and Kotlin.
In the Kotlin code I use Moshi to convert an object to Json in a convertor for a Room database table.
I have one case that works perfectly but another one produces the error:
Not enough information to infer type variable T
This is what my code looks like:
val type: Type = Types.newParameterizedType(
MutableMap::class.java,
LayerTwoConn::class.java,
TWeFiState::class.java,
WfMeasureFileMgr::class.java,
Traffic::class.java,
ThroughputCalculator::class.java,
CellSubTechThroughput::class.java,
LongValuesAverageCalculator::class.java,
LayerTwoConn.SenselessTraffic::class.java
)
val json = Moshi.Builder().build().adapter(type).toJson(layerTwoConn)
I have included all the classes that are used in the objects.
What have I missed?
This case works perfectly:
val type: Type = Types.newParameterizedType(
MutableList::class.java,
CnrScan::class.java,
)
val jsonAdapter: JsonAdapter<List<CnrScan>> = Moshi.Builder().build().adapter(type)
val json = jsonAdapter.toJson(list)
In this object, all the internally used classes are standard Java class and not my own.
Have I missed something simple?
I don't know if this is important but the class LayerTwoConn's constructor is private.
I think you are trying to convert too many classes into one type, try to convert MutableMap class and LayerTwoConn class.
Do note that Room uses SQL architecture, so try to predict what you want your table to contain

Kotlin and JSON Parsing

It's my first time dealing with JSON in android studio, and I'm trying to parse the response from google books API to JSON and then retrieve some info about the volume such as the title, author, and the description. the problem is there are JSON objects within the main JSON. Should I create a data class for each JSON?
the API link 'https://www.googleapis.com/books/v1/volumes?q=kotlin'
how i'm parsing it
val jsonResponse = gson.fromJson(body, JsonResponse::class.java)
the data classes that i created
data class JsonResponse
(val kind:String,val count:String,val items:List<JsonItems> )
data class JsonItems
(val kind:String,
val id:String,
val etag:String,
val self:String,
val volumeInfo:List<VolumeInfo>,
val saleInfo:List<SaleInfo>,
val accessInfo:ListList<AccessInfo> ,
val searchInfo:List<SearchInfo>)
is there any simpler solution to avoid unused data classes?
You need to have these classes to be able to parse the json, but then you can just create objects of other classes using those data (and drop the earlier instances).
Also, you don't need to include fields that you don't need.
You can parse without data classes and you can use other tools than Gson.
I had a use case similar to yours. I did not want to write data classes, but just get direct access to the specific data I was interested in from a larger nested JSON object.
I found a solution that worked well for me here
https://johncodeos.com/how-to-parse-json-in-android-using-kotlin/
using JSONTokener https://developer.android.com/reference/org/json/JSONTokener
Based on these examples, some code for parsing books might look like:
// response is a String with the JSON body
val jsonObject = JSONTokener(response).nextValue() as JSONObject
val items = jsonObject.getString("items") as JSONArray
for (i in 0 until items.length()) {
val volumeInfo = items.getJSONObject(i).getString("volumeInfo")
val title = volumeInfo.getString("title")
}
Another Stackoverflow post with other suggestions is here How to parse JSON in Kotlin?

Gson Pojo class

I'm making cryptocurrency information viewer now. I use Retrofit to get response, but have some problem.
https://api.coinone.co.kr/ticker/?currency=all
This is the api I use. and its structure is... too complicated to make pojo class.
if I make convert this to pojo class, then pojo class will be like
data class Coinone(
val result: String,
val errorCode: String,
val timestamp: String,
val crypto1: Ticker,
val crypto2: Ticker,
val crypto3: Ticker,
...
val cryptoN: Ticker)
Should I do like that? Is that only one solution?
I mean, if api data would be updated, I must update my pojo class. If I don't, apps will surely make crushes.
Is there any solution for making pojo class with api having similar structure?
Gson Object used when I don't know about Retrofit, GsonConverter and Kotlin not so much.

How to skip JSON properties in Moshi?

I'm trying to implement JSON parsing in my Android application written in Kotlin using com.squareup.moshi (v1.10.0).
Within the JSON file there are some properties that are not interesting in my case. Let's say, I only need the position to be able to mark the place on a map and the JSON looks like this:
"location":{
"address":{
"country":"..."
},
"position":{
"lat":47.469866,
"lon":19.062435
}
}
If I'm right, the data class in Kotlin should look like this if I'd like to parse that JSON:
#Parcelize
data class Location(
val address: Address,
val position: Position
): Parcelable
#Parcelize
data class Address(
val country: String
): Parcelable
#Parcelize
data class Position(
val lat: Double,
val lon: Double
): Parcelable
In Moshi's documentation I could find the transient keyword to skip values which in Kotlin works as an annotation (#Transient). As the documentation says:
Transient fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead it will get a default value.
Does it mean that if I don't want to have the address object, I should use the following code?
#Parcelize
data class Location(
#Transient val address: Address? = null,
val position: Position
): Parcelable
Also, what about in general terms? What if I have huge list of properties within a JSON object but I know I only need the 'position' object? Do I still have to create null values to parse the JSON file field-by-field?
I think you are looking for something similar to GSON's #Expose annotations, wherein all model fields are excluded from parsing except those annotated.
This functionality is currently not available in Moshi, so your current implementation using the #Transient annotation seems to be the most optimal solution. (See Moshi issues conversation here.)
Extra food for thought:
You may also wish to use #IgnoredOnParcel on your transient fields since you are implementing the parcelable interface. (Have a look here for some implementation pointers.)
Alternatively you could separate your data model into 2 models - one for use in your app and one which reflects the server (JSON) schema (just as you have done above). The main data model for your app (which could implement parcelable) would contain only the fields you use (for example, the position field). When you parse your data, you then convert that data to your primary data model using some simple adapter. (This is often good practice anyhow, since server-side schemas are inherent to change. This way, any changes in the JSON schema wouldn't end having any ripple effect throughout your code.)
https://github.com/square/moshi#omit-fields-with-transient
Omit fields with transient
Some models declare fields that shouldn’t be included in JSON. For example, suppose our blackjack hand has a total field with the sum of the cards:
public final class BlackjackHand {
private int total;
...
}
By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding JSON. Prevent a field from being included by adding Java’s transient keyword:
public final class BlackjackHand {
private transient int total;
...
}
Transient fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead it will get a default value.

Categories

Resources