fetching value of specific key from json response - android

how can I store a specific value of a key from json response into a variable
{
"results": [
{
"name": ryan,
"roll_id": 64,
"class_id": 310,
"net_id": 95,
},
],
};
above is the json response :-
val gson = GsonBuilder().create()
val ListV = gson.fromJson(body, HomeClass::class.java)
after these 2 lines I'm totally clueless how to do it I've gone through Internet but It was hard for me to understand how to proceed further.

Your Json Structure will be
{
"results": [
{
"name": "Amiyo",
"roll_id": 1,
"class_id": 10,
"net_id": 91
},
{
....
}
]
}
Data class should be
data class HomeClass (
#SerializedName("results") val results : List<Results>
)
data class Results (
#SerializedName("name") val name : String,
#SerializedName("roll_id") val roll_id : Int,
#SerializedName("class_id") val class_id : Int,
#SerializedName("net_id") val net_id : Int
)
fromJson
val listData = gson.fromJson(jsonData, HomeClass::class.java)
Then
val totalSize = 0 until listData!!.size
if(totalSize.size>0)
{
for (i in totalSize)
{
//Your Code i==Position
}
}

Related

Kotlin: JSON Schema type "number" gets converted to String when using JSONObject(String)

I implemented a function to validate a given JSONObject against a given JSON Schema JSONObject.
Here is the code:
class JsonSchemaUtilsTest {
//a JSON as a String
val testJsonStr : String =
"""
{
"duration": 28298080890890809809
}
""".trimIndent()
//The JSON schema as a String
val testJsonSchemaStr : String =
"""
{
"${'$'}schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"duration": {
"type": "number",
}
},
"required": [
"duration"
]
}
""".trimIndent()
#Test
public fun test() {
val json : JSONObject = JSONObject(testJsonStr)
val schemaJson : JSONObject = JSONObject(testJsonSchemaStr)
var boolean : Boolean = JsonSchemaUtils.validate(json, schemaJson)
//The following line fails with "org.everit.json.schema.ValidationException: #/duration: expected type: Number, found: String"
Assert.assertTrue("json validates against schemaJson", boolean)
}
}
Im using the https://github.com/everit-org/json-schema library.
So somehow the given Number "duration": 28298080890890809809 is converted to a String when using the JSONObject(String) constructor.
Thanks in Advance

how to handle two different Retrofit response in Kotlin?

I have tried to read this similar thread in java in here
so I try the accepted answer there but it doesn't work. here is my problem
I will get two different JSON Response from an endpoint. if I successfully get the Restaurant data, the JSON will be like this
{
"R": {
"has_menu_status": {
"delivery": -1,
"takeaway": -1
},
"res_id": 18941862,
"is_grocery_store": false
},
"id": "18941862",
"name": "Pizza Maru",
"url": "https://www.zomato.com/jakarta/pizza-maru-1-thamrin?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1",
"location": {
"address": "Grand Indonesia Mall, East Mall, Lantai 3A, Jl. M.H. Thamrin No. 1, Thamrin, Jakarta",
"locality": "Grand Indonesia Mall, Thamrin",
"city": "Jakarta",
"city_id": 74,
"latitude": "-6.1955810000",
"longitude": "106.8213770000",
"zipcode": "",
"country_id": 94,
"locality_verbose": "Grand Indonesia Mall, Thamrin, Jakarta"
},
"switch_to_order_menu": 0,
"cuisines": "Pizza",
"timings": "10 AM to 10 PM",
"average_cost_for_two": 180000,
"price_range": 3,
"currency": "IDR",
"thumb": "https://b.zmtcdn.com/data/pictures/chains/2/18941862/403aa36cb046e86a694e7989bb7cd545.jpg?fit=around%7C200%3A200&crop=200%3A200%3B%2A%2C%2A",
"has_online_delivery": 0,
"is_delivering_now": 0,
"store_type": "",
"phone_numbers": "021 3108656",
}
then If I send invalid restaurantID then I will get error JSON Response like this:
{
"code": 404,
"status": "Not Found",
"message": "Not Found"
}
here is the data class I made
data class Restaurant (
#SerializedName("id")
val id : Int = 0,
#SerializedName("name")
var name : String = "",
#SerializedName("url")
val url : String = "",
#SerializedName("location")
val location : Location = Location(),
#SerializedName("currency")
val currency : String = "",
#SerializedName("phone_numbers")
val phone_numbers : String = "",
#SerializedName("thumb")
val thumbnail : String = ""
)
for successful Response
data class Location (
#SerializedName("address")
val address : String = "",
#SerializedName("city")
val city : String = "",
#SerializedName("latitude")
val latitude : Double = 0.0,
#SerializedName("longitude")
val longitude : Double = 0.0,
#SerializedName("zipcode")
val zipcode : String = ""
)
for Error Response
data class ErrorResponse (
val code : Int,
val status : String,
val message : String
)
here is my Interface for my Retrofit. the idea is, I will cast it as Any first, then I will downcast either to Restaurant or ZomatoErrorResponse
interface RestaurantAPI {
#Headers("user-key: $USER_KEY_ZOMATO")
#GET("restaurant")
fun getRestaurantDetail(
#Query("res_id") id: Int
): Call<Any>
}
here is the error:
so I use my retrofit like this
val call = restaurantService.getRestaurantDetail(restaurantID)
call.enqueue(object: Callback<Any>{
override fun onResponse(call: Call<Any>, response: Response<Any>) {
if (response.isSuccessful) {
// this line is executed
Log.d("checkxxx","${response.body()}")
val restaurantData = response.body() as Restaurant // <-- but Error while casting Any to Restaurant in here
restaurant.postValue(restaurantData)
}
}
})
my app crash at that line. but actually I can successfully get the data, but I fail to cast it to Restaurant.
here the logcat of my response.body()
what went wrong in here ?
or maybe there is a better approach than this one
I finally can solve my problem using this code below
val call = restaurantService.getRestaurantDetail(restaurantID)
call.enqueue(object: Callback<Any>{
override fun onResponse(call: Call<Any>, response: Response<Any>) {
if (response.isSuccessful) {
val gson = Gson()
val restaurantData = gson.fromJson(gson.toJson(response.body()), Restaurant::class.java)
} else {
val errorBody = response.errorBody() ?: return
val type = object : TypeToken<ErrorResponse>() {}.type
val errorResponse: ErrorResponse? = gson.fromJson(errorBody.charStream(), type)
val errorMessage = errorResponse?.message ?: "Unknown Error"
}
}
})
don't forget to set the interface to be Any like this
interface RestaurantAPI {
#Headers("user-key: $USER_KEY_ZOMATO")
#GET("restaurant")
fun getRestaurantDetail(
#Query("res_id") id: Int
): Call<Any> // <---- set to Any like this
}
in my case, I have successful response and an error response. so I need to separate it like that.
but if you have 2 successful responses but it has different JSON then you need to perform null checking to restaurantData in my code above, if null then mapping it the other POJO.
You should use gson to convert json to an object
https://github.com/google/gson
Example
val gson = Gson();
val jsonInString = "{\"userId\":\"1\",\"userName\":\"Yasir\"}";
val user = gson.fromJson(jsonInString, User.class);

Storing a nested JSON object into Android Persistence Library

from my local Django Rest Framework service I get the following JSON output:
{
"count": 5,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"created": "2020-04-18T16:00:16.060915Z",
"name": "Germany",
"groups": [
{
"id": 1,
"created": "2020-04-18T16:03:11.138661Z",
"name": "MyGroup1",
"owner_id": 1
},
{
"id": 2,
"created": "2020-04-18T16:03:20.701660Z",
"name": "MyGroup2",
"owner_id": 1
},
...
Each Country can have many Groups. For this I have created the following data classes in my Android App project:
#JsonClass(generateAdapter = true)
data class NetworkCountryContainer(
val count: Long,
val next: String?,
val previous: String?,
val results: List<Country>
)
#Entity(tableName = "country_table")
#JsonClass(generateAdapter = true)
data class Country(
#PrimaryKey
#Json(name="id")
val countryId : Int,
#Json(name="name")
val countryName: String,
#Json(name="groups")
val groupList: List<Group> // <--- this field causes the ERROR
)
#Entity(tableName = "group_table")
#JsonClass(generateAdapter = true)
data class Group(
#PrimaryKey
#Json(name="id")
val groupId : Int,
#Json(name="name")
val groupName: String,
#Json(name="owner_id")
val ownerId: Int
)
Android Studio tells me this:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Why I need a TypeConverter ? And how can I build one ?

Parsing API data which contain object (Klaxon) (Kotlin)

I have API response which contain object (graphic)
[
{
"code": 200,
"status": "OK",
"FirstDay": "2019-11-18",
"LastDay": "2019-11-24",
"graphic": {
"2019-11-23": [
{
"godzinaStart": "08:30",
"godzinaStop": "10:00",
"przedmiot": "Matematyka dyskretna",
"sala": "32AK8",
"nauczyciel": "xxx",
"grupy": "1K131; 1K132; 1K133; 1K134; 1K135; 2K131",
"typ": "wykład"
},
],
"2019-11-24": [
{
"godzinaStart": "08:30",
"godzinaStop": "10:00",
"przedmiot": "Podstawy informatyki",
"sala": "308K",
"nauczyciel": "xxx",
"grupy": "1K131",
"typ": "laboratorium"
},
]
}
}
]
I have to parse this JSON to object in Kotlin. So i made class with parameters
class GraphicAPIResponse(
var code: Int,
var status: String,
var errorMessage: String = "",
var FirstDay: String = "",
var LastDay: String = "",
var graphic: JsonObject? = null OR var graphic: JsonArray<Any>? = null (I tried both)
)
I'm parsing data by this function
val responeAPI = Klaxon().parseArray<GraphicAPIResponse>(response)
When graphic is JsonObiect type appliaction throw error
I/System.out: ERROR -> Unable to instantiate JsonObject with parameters []
When graphic is JsonArray<Any> type, here's error
I/System.out: ERROR -> Unable to instantiate GraphicAPIResponse with parameters [LastDay: 2019-11-24, code: 200, status: OK, graphic: java.lang.Object#aef265a, FirstDay: 2019-11-18]
I'm trying to resolve the problem from 2 hours. Can someone help me please? :(
#EDIT
Thank You #Alexey Romanov
That help
Define a type for the nested object:
class Lesson(val godzinaStart: String, val godzinaStop: String, ...)
and use it in GraphicAPIResponse:
class GraphicAPIResponse(
var code: Int,
var status: String,
var errorMessage: String = "",
var FirstDay: String = "",
var LastDay: String = "",
var graphic: Map<String, Lesson> = mapOf()
)
(though honestly, I'd expect JsonObject to work as well)

How to parse dynamically named jsonarray using Gson and retrofit2?

I have a JSON response, which looks like this:
{
"equipment_layer": [
{
"id": 2,
"name": "Gateway",
"detail": "All gateways"
},
{
"id": 3,
"name": "Node",
"detail": "All Nodes"
},
{
"id": 1,
"name": "Miscellaneous",
"detail": "All miscellaneous assets"
},
{
"id": 4,
"name": "Sensors",
"detail": "All Sensors"
},
{
"id": 5,
"name": "IRM",
"detail": "Installation required material"
},
{
"id": 6,
"name": "Communication",
"detail": "All communication devices such as Cellular Router, ETU etc. which are purely communication"
}
],
"data": {
"1": [
{
"equipment_id": 353,
"item_quantity": 1,
"name": "DC Current Transformer (20mm) (Old)",
"shortcode": "SNS-DCI-CT20m-R1A",
"part_number": "718,804,805,",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
},
{
"equipment_id": 357,
"item_quantity": 1,
"name": "Fuel Sensor - B4 (Old)",
"shortcode": "SNS-FUL-PSR-R1A",
"part_number": "718,810,811",
"equipment_layer_id": 1,
"equipment_layer_name": "Miscellaneous"
}
],
"2": [
{
"equipment_id": 345,
"item_quantity": 1,
"name": "RTU (Old)",
"shortcode": "RAN-RTU-PMCL-R1A",
"part_number": "787,788,789",
"equipment_layer_id": 2,
"equipment_layer_name": "Gateway"
}
],
"3": [
{
"equipment_id": 356,
"item_quantity": 1,
"name": "Battery Analyzer (Product) (Old)",
"shortcode": "RAN-BAM-PMCL-R1A",
"part_number": "787,808,809",
"equipment_layer_id": 3,
"equipment_layer_name": "Node"
}
],
"4": [
{
"equipment_id": 346,
"item_quantity": 1,
"name": "DC Current Transformer (30mm) (Old)",
"shortcode": "SNS-CT-DCI-R1A",
"part_number": "718,792,793",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
},
{
"equipment_id": 350,
"item_quantity": 1,
"name": "AC Block CT (Old)",
"shortcode": "SNS-ACI-BLK-R1A",
"part_number": "718,790,791",
"equipment_layer_id": 4,
"equipment_layer_name": "Sensors"
}
]
}
}
Now the part after the "data" label is dynamic, in a response I can have subarrays of "1", "2" but not of "3" or "4".The POJO of the data inside is same as you can see. So how can I parse this data? I'm using Rerofit2 with Gson.converterfactory. I've tried jsonchema2pojo as well but the data inside the "data" object is not showing up.
I've tried to follow this method:
Parsing Retrofit2 result using Gson with different JSON structures but I can't seem to trigger the UnrwapConverter.
This is my converterfactory implementation:
internal class UnwrappingGsonConverterFactory private constructor(private val gson: Gson) : Converter.Factory() {
override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *> ?{
if (!needsUnwrapping(annotations)) {
return super.responseBodyConverter(type, annotations, retrofit)
}
val typeAdapter = gson.getAdapter(TypeToken.get(type))
return UnwrappingResponseConverter(typeAdapter)
}
private class UnwrappingResponseConverter (private val typeAdapter: TypeAdapter<*>) : Converter<ResponseBody, Any> , AnkoLogger{
#Throws(IOException::class)
override fun convert(responseBody: ResponseBody): Any? {
responseBody.use { responseBody ->
JsonReader(responseBody.charStream()).use({ jsonReader ->
// Checking if the JSON document current value is null
val token = jsonReader.peek()
if (token === JsonToken.NULL) {
return null
}
// If it's an object, expect `{`
jsonReader.beginObject()
var value: Any? = null
// And iterate over all properties
while (jsonReader.hasNext()) {
val data = jsonReader.nextName()
debug("Unwrap Stuff: $data")
when (data) {
"1", "2", "3", "4", "5", "6" -> value = typeAdapter.read(jsonReader)
else ->jsonReader.skipValue()
}
}
// Consume the object end `}`
jsonReader.endObject()
return value
})
}
}
}
companion object {
fun create(gson: Gson): Converter.Factory {
return UnwrappingGsonConverterFactory(gson)
}
private fun needsUnwrapping(annotations: Array<Annotation>): Boolean {
for (annotation in annotations) {
if (annotation is Unwrap) {
return true
}
}
return false
}
}
}
And the interface:
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class Unwrap
My data classes are these:
data class ActivityNodes(#SerializedName("equipment_layer") val equipmentLayer: List<EquipmentLayer>,
#SerializedName("data") val data: nodeData)
data class nodeData (#SerializedName("1") val nodeList: List<dataItem>) <-- this is where I need someway to tell SerializedName that the value can be anything from 1 to 6
data class dataItem(#SerializedName("equipment_id") val equipmentId: Int,
#SerializedName("item_quantity") val itemQuantity: Int,
#SerializedName("name") val name: String,
#SerializedName("shortcode") val shortCode: String,
#SerializedName("part_number") val partNumber: String,
#SerializedName("equipment_layer_id") val equipmentLayerId: Int,
#SerializedName("equipment_layer_name") val equipmentLayerName: String,
var isScanned: Boolean = false )
data class EquipmentLayer(#SerializedName("id") val id: Int,
#SerializedName("name") val name: String,
#SerializedName("detail") val details: String)
For the dynamic JSON, you have to parse the JSON string manually. To get JSON string from retrofit you have to use ScalarsConverterFactory instead of GsonConverterFactory.
Add this dependency:
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
Create Adapter like this:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://echo.jsontest.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
Create request method with ResponseBody
public interface MyService {
#GET("/key/value/one/two")
Call<ResponseBody> getData();
}
You can get Json String like this:
MyService service = retrofit.create(MyService.class);
Call<ResponseBody> result = service.getData();
result.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
try {
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Throwable t) {
e.printStackTrace();
}
});
Now you have to parse the JSON string manually to get your data from JSON.
Hope it helps:)
use below for the part of "data" of json:
Type mapType = new TypeToken<Map<String, List<EqupmentDetail.class>>>() {}.getType(); // define generic type
Map<String, List<EqupmentDetail.class>> result= gson.fromJson(new InputStreamReader(source), mapType);
here define EqipmentDetails class same as your refence
this will definitely work

Categories

Resources