Broken server response handling with Moshi - android

The expected json response from server should be :
{
"teacher": {
"123": {
"_id": "389",
"name": "test_fast_teacher1"
}
}
}
Server returned json with this :
{
"teacher": [
]
}
Anyway to handle this broken json response?
Before I switching from Gson, the teacher object will still be deserialised, just that it will be null. By using Moshi, the error would be threw and I can't proceed with the other json which is serialised correctly.
Please refer to the link for the reply from author.

How about something like this?
Moshi moshi = new Moshi.Builder()
.add(DefaultOnDataMismatchAdapter.newFactory(Teacher.class, null))
.build();
JsonAdapter<Teacher> adapter = moshi.adapter(Teacher.class);
Teacher teacher = adapter.fromJson(json);
// teacher == null
where DefaultOnDataMismatchAdapter is Jesse's code you can copy into your code base.
When the Teacher type comes back in an unexpected format that would produce a JsonDataException, it will default back to your set value (in this case, null).

Related

How to retrieve the list of all GitHub repositories of a person? parsing json list with no key value

I know this question has been answered at this post with same question. however, my question is the Json response by the most upvoted answer will be a json list with no key value.
(you can also check the sample json from the official github website)
I am using Moshi library to parse json. However, I have no idea how to parse that Json list whose key value is not set.(only a list present in the Json with no Key value for that list)
this is what it looks like though
[
{
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"...": "...",
...
},
{
"id": 1296255,
"node_id": "somevalue",
"...": "...",
}
...
]
since the purpose of asking this question is to get a list of repositories of a user, you could leave any code snippet to get that type of list Json to Kotlin data class.
I appreciate your help in advance.
Try this one for Array list
inline fun <reified T> Any?.getResponseInArrayList(): ArrayList<T?>? {
return try {
val token: TypeToken<ArrayList<T>> = object : TypeToken<ArrayList<T>>() {}
val json = Gson().toJson(this)
Gson().fromJson(json, token.type)
} catch (e: Exception) {
Log.e("GSON ERROR ", "" + e.message)
null
}
}
use like this
val model = yourJsonString.getResponseInArrayList<YouJsonModel>()

Consuming polymorphic json "data: { put_anything_here }" with Gson & Retrofit

I'm not sure if polymorphic is the right term to use so my apologies.
I'm working with the following API:
Request body:
{
"user_id": "user_id",
"command": "submit_document",
}
Response:
{
"result": "success",
"code": 200,
"status": "ok",
"screen": "documents_rejected", // This is unique for different `data`
"next_screen": "",
"message": "Successful",
"data": {
// `data` is always a json object with known fields and parameters
}
}
I have data classes ready for different types of data responses like:
data class PhoneData(
#SerializedName("phone_number")
val phoneNumber: String? = null,
#SerializedName("phone_status")
val phoneStatus: String? = null
)
for "screen": "phone" and the following for another screen:
data class Data(
val deepLink: String? = null
)
The problem is, at the start, I have to make the following request to retrieve the current screen:
{
"user_id": "user_id",
"command": "get_current_screen",
}
which returns a similar response as above:
{
"result": "success",
"code": 200,
"status": "ok",
"screen": "main_screen", // Different types of screen types are known.
"next_screen": "",
"message": "Successful",
"data": {
// `data` is always a json object but the object could contain anything depending on the `screen` type.
}
}
but the data field could contain anything depending on the screen
data class SplashScreenData(
// How do I make this data class combine all other data classes? One ugly approach is to add all the fields from different `data` classes here and use this one only.
)
I found about the RuntimeTypeAdapterFactory for polymorphic cases but am not sure how to make it work when there's no "type" like field within the data object (screen is unique but it's outside the data object).
It would be very helpful if someone has a solution or could point me in a direction.
val frameTextReceived: String = frame.readText()
val jsonObject = JsonParser.parseString(frameTextReceived).asJsonObject
val type = when (jsonObject.get("type").asString) {
TYPE_JOIN_ROOM -> JoinRoom::class.java
TYPE_GAME_MOVE -> GameMove::class.java
TYPE_DISCONNECT_REQUEST -> DisconnectRequest::class.java
else -> BaseModel::class.java
}
val payload = gson.fromJson(frameTextReceived, type)
This is my solution, here I have type parameter by which I can know in which class I have to deserialize the object but in your case you have screen parameter, you can use this.

How To Parse Response of Multiple Types of Single Key

How To Parse Response of Multiple Types?
Key is like (suppose student_list is a key of list types when student_list is empty then it makes as a string like student_list=""), How to manage this types of response using Retrofit? I am using MVVM Model with retrofit.
My Response:
when I get Data into the List
{
"status": 200,
"data": [
{
"prod_month_total": 2989.61,
"product": "GAS"
},
{
"prod_month_total": 39566.22,
"product": "OIL"
},
{
"prod_month_total": 83912.55,
"product": "OTHER"
}
]
}
when List is Empty Then Response:
{"status":404,"data":"No result found"}
I am getting this Error:
java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 23 path $.data
first create the right model calss use this site http://www.jsonschema2pojo.org
than use
`if( reponce.isSucessful){
if(responce.status==200){
//code here
}
else{
// find the error
}
}else
{
//
}`

Moshi Custom Adapter isn't being called

TL;DR:
My questions are:
1 - How can I make an Adapter for the "timestamp": 1515375392.225 to ZonedDateTime.
2 - How can I register the List<Report> adapter in the moshi object Builder if I need the moshi object to get this adapter, according to the documentation?
My JSON string has the following structure:
[
{
"id": 0,
"location": {
"latitude": -22.967049,
"longitude": -43.19096
},
"timestamp": 1515375392.225
},
{
"id": 0,
"location": {
"latitude": -22.965845,
"longitude": -43.191102
},
"timestamp": 1515375392.225
},
.......
}]
The timestamp is an automatic conversion made by the Jackson JavaTimeModule, it converts a ZonedDateTime to a timestamp String in the form of a decimal number representing the seconds and nanoseconds from an Instant.
In order to parse the JSON timestamp String, I made the following Moshi adapter:
public class ZonedDateTimeAdapter {
#FromJson ZonedDateTime fromJson(String timestamp) {
int decimalIndex = timestamp.indexOf('.');
long seconds = Long.parseLong(timestamp.substring(0, decimalIndex));
long nanoseconds = Long.parseLong(timestamp.substring(decimalIndex));
return Instant.ofEpochSecond(seconds, nanoseconds).atZone(ZoneId.systemDefault());
}
#ToJson String toJson(ZonedDateTime zonedDateTime) {
Instant instant = zonedDateTime.toInstant();
return instant.getEpochSecond() + "." + instant.getNano();
}
}
And then I register this adapter as:
Type type = Types.newParameterizedType(List.class, Report.class);
Moshi moshi = new Moshi.Builder().add(new ZonedDateTimeAdapter()).build();
JsonAdapter<List<Report>> reportAdapter = moshi.adapter(type);
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build();
The problem is, when I call my webservice using Retrofit, I get the following Exception:
com.squareup.moshi.JsonDataException: java.lang.NumberFormatException:
For input string: ".067000000" at $[0].timestamp
(keep in mind the nanoseconds .067000000 here won't be the same as the JSON example that I gave before, since they called the webservice at different times).
I tried to place a breakpoint on my ZonedDateTimeAdapter, but it's never being called. But it's influencing Moshi, because if I remove it from the Moshi.Builder, the error changes to:
Caused by: java.lang.IllegalArgumentException: Cannot serialize
abstract class org.threeten.bp.ZoneId
I also tried to change the ZonedDateTimeAdapter to deal with Double instead of String, but it just changes the error message to:
com.squareup.moshi.JsonDataException: java.lang.NumberFormatException:
For input string: ".515376840747E9" at $[0].timestamp
So, basically, I have a bunch of changing error messages and no idea what am I doing wrong. I followed the Moshi documentation on Custom Adapters and I don't know what else to do.
Your JSON adapter’s #ToJson methods is accepting a String but the timestamp is a number. Either change this to be a number (ie. double) or pass a JsonReader instead of a String and read the number out yourself. In this case you can call reader.nextString().

Deserialise JSON with dynamic field

I'm loading JSON with a completely dynamic field from an API using Retrofit on Android.
This is an example:
{
"success": true,
"messages": {
"success": [
"SUCCESS_MESSAGE"
],
"error": {
"specific_error": {
…
}
}
},
"session": {
"id": "fj4qewqrewq43fdsa",
"expire": null,
"YII_CSRF_TOKEN": "fda432fdafasd78978fdas"
},
"metadata": {…}
}
The structure of the messages object can change with every request. Sometimes there is the success field and sometimes not. Same goes for the error field. The error field especially can be an array but also an object with several other objects and fields inside.
Does it make sense to deserialise the messages field into different POJOs and if so how would I do that? Would it make more sense to keep the field as a JSON object and get values from the object when they are needed?

Categories

Resources