I am accessing a API that has geometries object types in an JSON Object. The issue I have is in my Kotlin model class (below), I cannot have two Object Types for the node, as I get hit with the error
java.lang.IllegalStateException: Expected a double but was BEGIN_ARRAY at line 12 column 16 path $.features[0].geometry.geometries[0].coordinates[0];
because for the each inner node coordinates ofeach object, it can either return:
List<Double>
List<List<Double>>
API JSON
"geometries": [
{
"type": "Point",
"coordinates": [153.0533903,-26.7735391]
},
{
"type": "LineString",
"coordinates": [
[153.0258962, -27.3399828],
[153.02596, -27.34007],
[153.02602, -27.34015],
[153.026028, -27.3401745]
]
}
]
...
...
...
The external API response is passed through Retrofit models, however in my model class how do provide both List<Double> & List<List<Double>> processing
public class EventsGeometriesAPINodeModel {
#SerializedName("type")
#Expose
internal var type: String? = null
//how do provide both List<Double> List<List<Double>> processing
//API retruns either:
//List<Double>
//List<List<Double>>
#SerializedName("coordinates")
#Expose
internal var coordinates: List<Double>? = null
}
You have to make coordinates variable dynamic like JsonArray and whenever you receive data then you have to check type of that variable.
val listCoordinates = jsonObject.coordinates
if(listCoordinates is List<Double>){
} else if(listCoordinates is List<List<Double>>){
}
Related
I have this json response from the api, and the response can't be change
data class Weather (
val category: String,
val id: String,
val meta: Meta
)
data class Meta (
val id: String,
val name: String,
val details: String
)
Json respose
{
"weather" : {
"category": "articles",
"id": "1",
"meta": {
"id": "1",
"name": "The shortest article. Ever.",
"details": "see"
},
"weather" : {
"category": "articles",
"id": "2",
"meta": []
}
If meta is empty, it come with an array but if not empty, it come with object.
Retrofit throws
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY
the api can't be modify so this has to be fix on client end. How can I solve this
You can not make this possible
meta object must be an object when have value and null when do not have any value, or an array with value when exist and empty when not exist.
meta can not be an array and object in the same time.
this is very bad mistake from who created this response body.
You can use Any type for meta. and put check at your code level like this.
data class Weather (
val category: String,
val id: String,
val meta: Any
)
if(meta is Meta)
parse it to your Meta object
else
parse it to list
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.
I am developing an Android App via Kotlin. The app uses Retrofit2 for HTTP requests. I used Retrofit2 over and over but I don't know how to solve this scenario.
I want to use an API which needs a query like query1, query2 etc. (Close to 100 values available)
For Example:
When I send a request via "query1" the response object has coordinates: List<List<List<Double>>>
When I send a request via "query2" the response object has coordinates: List<List<Double>>
When I send a request via "query3" the response object has coordinates: List<List<List<List<Double>>>>
When I send a request via "query4" the response object has coordinates: List<List<Double>>
I don't know how API returns the inner list count.
By the way there is just one coordinates in the object.
The response object in JSON format
[
{
"key-1": "value-1",
"key-2": "value-2",
"key-3": "value-3",
"key-4": {
"inner-key": [
[
[
1.0,
1.0
],
[
1.0,
1.0
]
]
]
}
},
{
"key-1": "value-1",
"key-2": "value-2",
"key-3": "value-3",
"key-4": {
"inner-key": [
[
[
[
1.0,
1.0
],
[
1.0,
1.0
]
],
[
[
1.0,
1.0
],
[
1.0,
1.0
]
]
]
]
}
}
]
Here is my data classes. But I don't know how can I define the "key4" object.
data class Response(
val key1: String? = null,
val key2: String? = null,
val key3: String? = null,
val key4: key4? = null,
)
data class key4(
//How should I define the "inner-key" object
)
Any suggestions would be helpful.
Solution:
I tried a few way for solution. I didn't know it's perfect solution but there is my solution:
There is my data class which name is key4 on my question.
data class key4(
val inner-key: List<Any>? = null
)
Also I cast inner-key like:
val temp = response?.key4?.inner-key as List<List<Double>>?
or
val temp = response?.key4?.inner-key as List<List<List<Double>>>?
I have this response, i have problem when i want to convert to pojo.
"equity": {
"0": {
"name": [
"Abc"
],
"code": [
"3410"
],
"ending_balance": [
301834470
]
},
"1": {
"name": [
"Xyz"
],
"code": [
"2180"
],
"ending_balance": [
0
]
},
"2": {
"name": [
"Pqr"
],
"code": [
"9220"
],
"ending_balance": [
0
]
},
"total_equity": 301834470
}
}
I'm confused about giving the right data type, because there are arrays("0","1","2") that contain objects and "total_equity" that contain number.
I've tried to give the map data type, but it will be error for "total_equity"
var equity: Map<String, EquityDto?>
If you know the solution for this problem please help me. Thank you
You can use
var equity: Map<String, Any>
While accessing the Any Data type u can compare the varrriable type(instance of) and use the value as following
if (valueofMap is Int){
//here is integer value
}
if (valueofMap is yourDataClass){
//your custom class
}
There can be a solution to this, but it will be a lengthy one.
One solution can be to transform the response to Map<String, Any> then you will have to check the type every time you have to use it and it can really annoying when you are using it in multiple classes.
Another solution can be to create a custom Custom Type Adapter which you can pass to the Retrofit Instance in the addConverterFactory method.
To create a custom adapter, you just have to follow the following steps:
Create a model in which you want to store the data. In your case it can be :
data class ApiResponse(
val data: Map<String, EquityDto?>,
val totalEquity:Int
)
Create the Adapter:
class StudentAdapter : TypeAdapter<ApiResponse?>() {
#Throws(IOException::class)
fun read(reader: JsonReader): ApiResponse {
val student:ApiResponse? = null
reader.beginObject()
var fieldname: String? = null
while (reader.hasNext()) {
var token = reader.peek()
if (token == JsonToken.NAME) {
//get the current token
fieldname = reader.nextName()
}
if ("total_equity" == fieldname) {
token = reader.peek()
student?.totalEquity = reader.nextInt()
}else {
token = reader.peek()
student?.data?.set(fieldname.toString(), reader.nextString())
}
}
reader.endObject()
return student
}
#Throws(IOException::class)
fun write(writer: JsonWriter, student: ApiResponse) {
writer.beginObject()
writer.name("data")
writer.value(student.data.toString())
writer.name("totalEquity")
writer.value(student.totalEquity)
writer.endObject()
}
}
If you know a better way to create a type adapter then you can surely use that.
So I made an api in laravel and it returns a response like this:
{
"message": "The given data was invalid.",
"errors": {
"email": [
"The email has already been taken."
],
"mobile": [
"The mobile has already been taken."
]
}
}
Can somebody show me how to get the specific values from errors?
You may create model representing your error json and use Gson to parse it. Here is some short example.
data class Errors(
val email: List<String>,
val phone: List<String>
)
data class YourErrorModel(
val message: String,
val errors: Errors
)
fun parseError(response: Response<*>): YourErrorModel? {
val errorBody = response.errorBody()?.string() ?: return null //No error body present
return Gson().fromJson(errorBody, YourErrorModel::class.java)
}
Also don't forget to handle nullable types in your response. And i suggest you to return just string, not array if that is exact error for field.
How about this :
JSONObject errorObject = yourJSONObject.optJSONObject("errors");
if (errorObject != null){
JSONArray emailMsgArray = errorObject.getJSONArray("email");
JSONArray mobileMsgArray = errorObject.getJSONArray("mobile");
String emailMsg= emailMsgArray.getString(0);
String mobileMsg= mobileMsgArray .getString(0);
}