How can I deserialize this? - android

I have a JSON response of an API REST call that I am not pretty sure how should I deserialize...
{
.....
"date": "10-10-19",
"rates": {
"GBP" : 101.01,
"EUR" : 102.01,
"AUD" : 103.4,
......
}
}
I would like to know How could I deserialize the "rates" object?. I think it was a Map object so using Gson I make the next POJO:
class POJO(
private val base: String,
private val date: Date,
private val rates: Rate
)
And my Rate class is
class Rate ( private val currency : Map <String, Double> )
It doesn't make any problem unless I try to use this Map in my class. When I try to access to this variable for example here :
view?.converterBinder!!.setCurrencyList(it.data!!.rates.currency)
currency is null because I think Gson doesn't know how to resolve it. I don't know if I had to deserialize it manually or there are any solution for this using Gson.
Any thoughts??

The provided Json is completely wrong, this is how it should be formatted
{
"date": "10-10-19",
"rates": {
"GBP": 101.01,
"EUR": 102.01
}
}
Please check with https://jsonlint.com to confirm the validity of a Json.
So you have a json object with a String "date", then you have another json object called "rates" containing 2 numeric Doubles "GBP" and "EUR".
Each Json should be represented by a class, so to parse it create the following object containing the 2 classes
object Models {
data class Rates(#SerializedName("GBP") val gbp: Double,
#SerializedName("EUR") val eur: Double)
data class ExchangeRates(#SerializedName("date") val date: String,
#SerializedName("rates") val rates: Rates)
}
Now you pass the class ExchangeRates to Gson to deserialize your object and you should have all the data in place.

Related

Retrofit + GSON + Room Parse subobject as one string

I'm using an API to retrieve some data, and store them in my app DB using Room, Retrofit2, and GSON.
My data object is as follow:
#Entity(tableName = "department")
data class Department(
val nom: String,
#PrimaryKey val code: String,
val region: String
)
And this is what the API returns me
{
"nom": "Ain",
"code": "01",
"region": {
"code": "84",
"nom": "Auvergne-Rhône-Alpes"
}
}
I want to transform the response region.nom as the data region field. My actual solution is to make an interface object that can store the response, then a function for mapping this interface to my data object. But i'm pretty sure there is better/cleaner solution to achieve this (like maybe TypeConverter, but can't understand how it works).
Thanks :
Assume your retrofit api response object name "response".You can simply do this :
var department = Department(response.nom,
response.region.nom)
Then just pass the "department" object to room db insert function.

Android: How to parse nested json array with different data type objects using retrofit - moshi?

I want to parse nested json using retrofit moshi. The json data i'm having is an array, inside array first element is string & second is again an array.
I don't want to parse first element in the array ("list"), just want to parse second element from the array (i.e. Inner array).
But i'm facing challenges exactly here with the data object to be use.
Obviously we can use Any type in kotlin with list, but again will loose type of the object inside the list.
Json Format:
{
"results": [
"list",
[
{
//jsonobj
},
{
//jsonobj
}
]
]
}
I want to parse json with this Data class that i have created without parsing first element & parse directly second element.
#JsonClass(generateAdapter = true)
data class ResponseModel(
#Json(name = "results")
val results: List<Results?>?,
) {
#JsonClass(generateAdapter = true)
data class Results(
#Json(name = "reference")
val reference: String?,
#Json(name = "enabled")
val enabled: Boolean,
)
}
fromJson overridden function
#Throws(IOException::class)
override fun fromJson(reader: JsonReader): C {
val result = newCollection()
reader.beginArray()
while (reader.hasNext()) {
result?.add(elementAdapter.fromJson(reader)!!)
}
reader.endArray()
return result
}
This is throwing an exception saying EXPECTED_OBJECT but it was STRING when trying to parse first element ("list").
i'm stuck here & not able to proceed further.
So can somebody plz help me to get out from here? any help will be appreciated.

How to handle dynamic changing JSON object in android

I have a JSON from an API which returns something this like this -
{
"amount": "10.0000",
"base_currency_code": "EUR",
"base_currency_name": "Euro",
"rates": {
"GBP": {
"currency_name": "Pound Sterling",
"rate": "0.9060",
"rate_for_amount": "9.0601"
}
},
"status": "success",
"updated_date": "2020-12-03"
}
From the data above, the value of json Object GBP changes depending on the currency code selected.
e.g if USD was selected by the user, that will change to USD
So, I copied the code and converted the JSON to kotlin data class using a plugin -
ApiResponse.class
data class ApiResponse(
val amount: String,
val base_currency_code: String,
val base_currency_name: String,
val rates: Rates,
val status: String,
val updated_date: String
)
data class Rates(
val GBP: GBP
)
data class GBP(
val currency_name: String,
val rate: String,
val rate_for_amount: String
)
As you can see, it generated a static class for the JSON object GBP which changes.
How can I generate my POJO class specifying that the JSON object "GBP" changes based on what is selected.
I am using Retrofit and GSON converter.
If You're using GSON that's better use JsonDeserializer<T> for Custom Parse models.
private class GBPDeserializer implements JsonDeserializer<GBP> {
#Override
public GBP deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
//TODO: You should JsonElement convert to JSONObject and by keys()
method run a dynamically loop and resolve fileds.
}
}

Kotlin Deserialization - JSON Array to multiple different objects

I'm using the 1.0.0 version of kotlin serialization but I'm stuck when I try to deserialize a "flexible" array.
From the Backend API that I don't control I get back an JSON Array that holds different types of objects. How would you deserialize them using kotlin serialization?
Example
This is the API's response
[
{
"id": "test",
"person": "person",
"lastTime": "lastTime",
"expert": "pro"
},
{
"id": "test",
"person": "person",
"period": "period",
"value": 1
}
]
#Serializable
sealed class Base {
#SerialName("id")
abstract val id: String
#SerialName("person")
abstract val person: String
}
#Serializable
data class ObjectA (
#SerialName("id") override val id: String,
#SerialName("title") override val title: String,
#SerialName("lastTime") val lastTime: String,
#SerialName("expert") val expert: String
) : Base()
#Serializable
data class ObjectB (
#SerialName("id") override val id: String,
#SerialName("title") override val title: String,
#SerialName("period") val period: String,
#SerialName("value") val value: Int
) : Base()
Performing the following code result in an error
println(Json.decodeFromString<List<Base>>(json))
error Polymorphic serializer was not found for class discriminator
When you say you don't control the API, is that JSON being generated from your code by the Kotlin serialization library? Or is it something else you want to wrangle into your own types?
By default sealed classes are handled by adding a type field to the JSON, which you have in your objects, but it's a property in your Base class. In the next example it shows you how you can add a #SerialName("owned") annotation to say what type value each class corresponds to, which might help you if you can add the right one to your classes? Although in your JSON example both objects have "type" as their type...
If you can't nudge the API response into the right places, you might have to write a custom serializer (it's the deserialize part you care about) to parse things and identify what each object looks like, and construct the appropriate one.
(I don't know a huge amount about the library or anything, just trying to give you some stuff to look at, see if it helps!)
#cactustictacs solution came very close. He said that "By default sealed classes are handled by adding a type field to the JSON"
But because I didn't had a type property I needed a other field that decides which subclass it should be.
In Kotlin Serializer you can do that by
val format = Json {
classDiscriminator = "PROPERTY_THAT_DEFINES_THE_SUBCLASS"
}
val contentType = MediaType.get("application/json")
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(format.asConverterFactory(contentType))
.build()
where in classDiscriminator you can enter the property that you want. Hope this helps other people in the future.

Android App crashes as Json element is empty String ("") and not Object

I am working on an android project and using RxAndroid, Retrofit to make API call and retrieve json. The json looks something like following :
{
"result": [
{
"parent": "jhasj",
"u_deviation": "skasks",
"caused_by": "ksks",
"u_owner_mi": {
"link": "https://gddhdd.service-now.com/api/now/v1/table/sys_user/ghtytu",
"value": "ghtytu"
},
"impact": "",
}
]
}
I am using gson to parse the Json. The problem is "u_owner_mi" sometimes reruns empty string "" when there is no value assigned to it. I don't have access to change the return type to null. This is making my app crash as I am expecting an object here.
I get the following error :
Expected BEGIN_OBJECT but was STRING
If you can't modify the server, try replacing the offending line in the server response before passing it to the Gson parser. Something like:
String safeResponse = serverResponse.replace("\"u_owner_mi\": \"\"", "\"u_owner_mi\": null");
Your app (client) code is expecting an object according to a contract specified in the class that you pass to GSON. Your app behaves as it should and crashes loudly. You should consider having your server return "u_owner_mi" : null instead of an empty string, assuming you have control over that. The u_owner_mi field on the client side would have to be a nullable type.
If you don't have the ability to fix the api, you could also write a custom deserializer.
Suppose your result class and sub-object are:
data class Result(
val parent: String,
val owner: Any?
)
data class Owner(
val link: String,
val value: String
)
The deserializer could be:
class ResultDeserializer : JsonDeserializer<Result> {
override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Result {
val jsonObject = json.asJsonObject
val ownerProperty = jsonObject.get("owner")
return Result(
parent = jsonObject.get("parent").asString,
owner = if (ownerProperty.isJsonObject) context?.deserialize<Owner>(ownerProperty.asJsonObject, Owner::class.java)
else ownerProperty.asString
)
}
}
Finally, to add the deserializer:
#Test
fun deserialization() {
val gson = GsonBuilder().registerTypeAdapter(Result::class.java, ResultDeserializer()).create()
val result1 = gson.fromJson<Result>(jsonWithObject, Result::class.java)
val result2 = gson.fromJson<Result>(jsonWithEmpty, Result::class.java)
}

Categories

Resources