Kotlin Json Question Expected a string but was BEGIN_OBJECT at path - android

Trying some different methods to parse nested Json that is less than user friendly. With the logger I can see the result coming in correctly but the log shows error
com.squareup.moshi.JsonDataException: Expected a string but was BEGIN_OBJECT at path $.capabilities[1]
I cannot for the life of me figure out how to parse the Attribute array. I have tried doing <List<Attribute>> and Attribute and it does not change the result. Is there a way to convert the Attribute array into a list?
Very new at coding in Android so looking for some help.
JSON to parse
{
"id": "65",
"name": "Switch - Kitchen",
"label": "Switch - Kitchen",
"attributes": [
{
"name": "switch",
"currentValue": "off",
"dataType": "ENUM",
"values": [
"on",
"off"
]
}
],
"capabilities": [
"Switch",
{
"attributes": [
{
"name": "switch",
"dataType": null
}
]
},
"Configuration",
"Refresh",
"Actuator"
],
"commands": [
"configure",
"flash",
"off",
"on",
"refresh",
"refresh"
]
}
DeviceDetails
data class DeviceDetails(
#Json(name="CapabilitiesList")
var attributeList: Attribute,
#Json(name="CapabilitiesList")
val capabilities: List<String>,
#Json(name="CommandsList")
val commands: List<String>,
var id: String = "",
var label: String = "",
var name: String = ""
)
data class Attribute(
val currentValue: String,
val dataType: String,
val name: String,
#Json(name="AttributesValues")
val values: List<String>
)
DeviceDetailsAPI
interface DeviceDetailsAPI {
#GET("devices/65")
fun getDeviceDetails(#Query("access_token") access_token: String):
Deferred<DeviceDetails>
companion object{
operator fun invoke(): DeviceDetailsAPI {
//Debugging URL//
val interceptor : HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY }
val client : OkHttpClient = OkHttpClient.Builder().apply {
this.addInterceptor(interceptor)}.build()
//Debugging URL//
val okHttpClient = OkHttpClient.Builder()
.build()
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://xxx.xxx.xxx.xxx/apps/api/109/")
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.addConverterFactory(MoshiConverterFactory.create())
.client(client)
.build()
.create(DeviceDetailsAPI::class.java)
}
}
}
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val API_KEY = "xxxxxxxx"
val testapiService = DeviceListAPI()
val testapiDetails = DeviceDetailsAPI()
//GlobalScope.launch (Dispatchers.Main) {
//val DeviceListAPI = testapiService.getAllDevices(access_token = API_KEY).await()
//textViewID.text = DeviceListAPI.toString()
//}
GlobalScope.launch (Dispatchers.Main) {
val DeviceDetailsAPI = testapiDetails.getDeviceDetails(access_token = API_KEY).await()
textViewID.text = DeviceDetailsAPI.toString()
}
}
}

The apparent problem is that the "capabilities": ... in the JSON block is a mixed type list, but you declare it as val capabilities: List<String>. Hence it fails when it hits the
{
"attributes": [
{
"name": "switch",
"dataType": null
}
]
},
item. It's hard to guess how this item relates to the capabilities, but as it currently stands it looks like this will require a pretty complicated custom Moshi adapter to be able to parse this into a meaningful data structure.

Related

Moshi: Getting Null values to pass through Retrofit 2 from an API Response

The API I am calling has a response that looks like this...
[
{
"id": 755,
"listId": 2,
"name": ""
},
{
"id": 203,
"listId": 2,
"name": ""
},
{
"id": 684,
"listId": 1,
"name": "Item 684"
},
{
"id": 276,
"listId": 1,
"name": "Item 276"
},
{
"id": 736,
"listId": 3,
"name": null
},
{
"id": 926,
"listId": 4,
"name": null
}
]
There are null values inside the response, but I can't get the API call to work because of it. I tried having the 'name' field be to accept a null value, but I got an error. I heard that Moshi (the converter I am using) could have a way to serialize nulls, so that they pass, but not sure how to go about it.
Here is some more code for a better understanding
Network File
interface InfoCollections {
#GET(ENDPOINT)
suspend fun getInfoService(): Response<List<GetInfoJsonResponse>>
}
object NetworkingObject {
val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
val networkingService: InfoCollections by lazy {
retrofit.create(InfoCollections::class.java)
}
val apiClient = ApiClient(networkingService)
The Repository
class InfoRepository(private val infoDao: InfoDao) {
private val allInfoFeeds: LiveData<List<GetInfoJsonResponse>> = infoDao.getAllInfo()
private val _infoFeeds: MediatorLiveData<List<GetInfoJsonResponse>> = MediatorLiveData()
val feeds: LiveData<List<GetInfoJsonResponse>>
get() = _infoFeeds
init {
_infoFeeds.addSource(allInfoFeeds){
_infoFeeds.value = it
}
}
suspend fun fetchInfo(): List<GetInfoJsonResponse>? {
val request = NetworkingObject.apiClient.fetchInfoJsonResponse()
if(request.isSuccessful){
infoDao.insertAll(*request.body()!!.toTypedArray())
return request.body()
}
return null
}
}
The DAO
#Dao
interface InfoDao {
#Query("SELECT * FROM info_response")
fun getAllInfo(): LiveData<List<GetInfoJsonResponse>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(vararg info: GetInfoJsonResponse)
}
The Data Class
#Parcelize
#Entity(tableName = "info_response")
data class GetInfoJsonResponse(
#PrimaryKey
val id: Int,
val listId: Int,
val name: String
): Parcelable
Here is the error message
Appreciate the help guys, thank you.

How to use Retrofit2 to GET list of addresses with the postcode JSON body

I'm using Retrofit2 for the first time, so I'm confused how to proceed. I have the following code
fun getAddressFromPostCode(postCode: String): List<PXAddress>{
val trimmedPostCode = postCode.replace("\\s".toRegex(),"").trim()
val dataBody = JSONObject("""{"postCode":"$trimmedPostCode"}""").toString()
val hmac = HMAC()
val hmacResult = hmac.sign(RequestConstants.CSSecretKey, dataBody)
val body = JSONObject("""{"data":"$dataBody", "data_signature":"$hmacResult"}""").toString()
val url = RequestConstants.getAddress
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(url)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
val address: PXAddress = retrofit.create(PXAddress::class.java)
}
with the idea that body needs to look like this:
"data":{
"postcode": "WA1 1LD"
},
"data_signature": "{{getSignature}}"
}
and the response should be
"success": 1,
"addresses": [
{
"address1": "47 Museum Street",
"address2": null,
"address3": null,
"town": "WARRINGTON",
"county": "",
"postcode": "WA1 1LD"
},
{
"address1": "49a Museum Street",
"address2": null,
"address3": null,
"town": "WARRINGTON",
"county": "",
"postcode": "WA1 1LD"
},
{
"address1": "Leda Recruitment",
"address2": "49 Museum Street",
"address3": null,
"town": "WARRINGTON",
"county": "",
"postcode": "WA1 1LD"
}
]
}
And I need to convert that response into a list of PXAddress which is
open class PXAddress : RealmObject() {
var addressLine1: String? = null
var addressLine2: String? = null
var addressLine3: String? = null
var town: String? = null
var county: String? = null
var postcode: String? = null
}
Your implementation is wrong for some reasons:
Use an interface to define the web service request, you must define a class like this:
interface ApiService {
#POST("your/webservice/path")
fun getPXAddress(#Body dataBody: YourBodyModel): Call<List<PXAddress>>
}
You must call your webservice with a data class as body, the gson converter will convert your models in json, in your main code you must do that:
fun getAddressFromPostCode(postCode: String): List<PXAddress>{
val trimmedPostCode = postCode.replace("\\s".toRegex(),"").trim()
val dataBody = DataBodyObject(postCode = trimmedPostCode)
val hmac = HMAC()
val hmacResult = hmac.sign(RequestConstants.CSSecretKey, dataBody)
val yourBodyModel = YourBodyModel(data = dataBody, data_signature = hmacResult)
val url = RequestConstants.getUrl() // This address must be only the host, the path is specified in ApiService interface
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
val api: ApiService = retrofit.create(ApiService::class.java) // With this you create a instance of your apiservice
val myCall: Call<List<PXAddress>> = api.getPXAddress(yourBodyModel) //with this you can call your service synchronous
}
One last thing, you must call your method asynchronous mode with rxjava, livedata or coroutines. All of them offers converters to retrofit. By default, retrofit has a call method like the example that I show you, you can complete your code doing this:
myCall.enqueue(object : Callback<List<PXAddress>> {
override fun onFailure(call: Call<List<PXAddress>>?, t: Throwable?) {
// Error response
}
override fun onResponse(call: Call<List<PXAddress>>?, response: Response<List<PXAddress>>?) {
// Success response
val myList : List<PXAddress> = response?.body
}
})
Best regards!

Moshi Date Adapter begin_object but was begin_array

I'm using the retrofit and moshi library in my project to help me connect to my backend. From there, I sent dates back but apparently, moshi can't handle dates. I've written my own JsonAdapter but now I get the error:
com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $
DateAdapter:
class DateAdapter: JsonAdapter<Date>() {
#FromJson
override fun fromJson(reader: JsonReader): Date? {
val value = reader.nextString()
return SimpleDateFormat("yyyy-MM-dd", Locale.FRENCH).parse(value)
//return getDateInstance(DateFormat.LONG, Locale.FRENCH).parse(value)
}
#ToJson
override fun toJson(writer: JsonWriter, value: Date?) {
writer.value(SimpleDateFormat("yyyy-MM-dd", Locale.FRENCH).format(value))
}
}
Network layer
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.add(DateAdapter())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
// the request that throws the error
#GET("getPartiesNearYou")
fun getPatiesNearYou(
#Query("distance") distance: Int,
#Query("lat") lat: Double,
#Query("long") long: Double,
#Query("userId") userId: String
): Deferred<NetworkPartyContainer>
Example response:
[
{
"location": {
"type": "Point",
"coordinates": [
50,
50
]
},
"participants": [
"5db76b7430957f0ef05e73fa"
],
"declines": [
null,
"5dc322e02c7171369e4c67fb"
],
"_id": "5dc322712c7171369e4c67fa",
"name": "Mout's Hartenjagen Party",
"date": "2019-11-28T23:00:00.000Z",
"maxSize": 4,
"gameId": "5db76b7430957f0ef05e73fa",
"createdAt": "2019-11-06T19:43:45.544Z",
"updatedAt": "2019-11-06T19:49:07.599Z",
"__v": 0
}
]
I've done some research and most talk about the fact that you get an array instead of a single object and that something needs to change but I don't know what or to add #Wrapped
your json is starting with array so you have to set your retrofit response in array like this in interface (This ans in JAVA) :- Call<List<ItemList>> getHomeContent();

Gson custom deserializer when it contains object or json string

How I can I write custom deserializer for same object? Or create 2 Objects (one which contains raw json string and another - Object)?
I'm getting "all device list" from server, and "updated device list" from server. Problem is that when I getting "all device list" I'm getting "Sensor", "Services", "Tail" as objects, but when getting "updated device list" that fields comes as strings (json strings). I will add photos to clarify issue.
All devices response:
[All devices] https://lh3.googleusercontent.com/lzhJXviqtW2l0pLHkJ9-2VBck9hnmREdfTlWTMF8BYTklbXR90jDe2AZbG9sHHPynCXh3HWlFQBsFBiL_oke-AMy-79zIqUsW8HVT7CrulA_KKyItARfwbRxWm8LWBspEriJEsfT4_AgNR7uceyO9pH4VAIm9LBBcuvoFOMJmvSt-y85dgqPld3WJ0RwIlQOBv3rqiw6Ci4QOC983LpiJAKgTgshsQeGNKUWnBIbzkJr87vtF54XOf4PsdAqv3IuRczQSeNLllPTIEaz6rylCotcgANWShMcpm-hQdlrt7E0VBygGTZGLW4hoOQn2F4DWH8p-QH2pEb7JKtEopha0q5cyISbK6AQSYHKMpYb7E8I_FKoxbQIEsvvjDPwGezZlloI1r6HnRHDpAaL8a2eF3yZ3ABTORHS8fa5UY___nvE1cppgzhuK9a1pFFX_A7yZQN_zQNM58i5hW6q0MhawbfFyY8NXBtTOujxz_NIg4_qP3Eu58Eb-wwz17pxYfpAbPRiDXrQDgaXPSmOrJ_txvdzP910UcxHZqFNiW_k3DXUN2tzCY2XSuyyZgc2eX-ZWaE0tevqsvdiCJSd2OvNBC8ihmZUTT0mreyZy-ii8xi2uHjN0KCtvmAkpy-XlKU1lddoxlwj_IrZqbSH_UPdqF4sa5tEahQ=w598-h1336-no
Updated devices response:
[updated devices] https://lh3.googleusercontent.com/-5VlMyTbTCmLcc14oWbaE5QViCio1V3NlcXQrrApZatE8K3CnW4flCGTbpncp_rbFnhjYq49WBqKBBD4g8TBwbHvZe1qsUZSW4JaTr1ZVJFCiLz1fl6SMredI7XnYhbm85q4-RxwxaEP90X6QAR7JOqZICk1QWTS0KxTp3XckHP45ca02LHicNO4TuB72_M1-vx4FMoAZQ42vSrnKZsHBqprJnsthS3ZoG9_v72xgjXPFRb144sRb0PPn6L9VbphZPbBhcg7WU68LK6eZgqyjIByxnkAlh-tsOLsWfMoze1Df93x9OI6OqOao8cphZD_bot-AViErtnc_vyNLHaDniJ6G6gUdDgco6rNCgyPvhGIcPbKf-wWdB8ENQ47HeJVxuJ0E9EE7rGRFhPchR1FxGM2nHewyoicds6uS2eCVpiKjtnen6TuTDfx3aH9NRUzL3thvN_9Jengh_XsyMPpvhGGHAUEcpYraHEXdtC25X9t5zpB1FhLzDiWVIZwhwihOCVI-5J-VBi7JqCOm9eaagYFunZSQI6U3u1qK1SZZfCarzxm_1dCYUECBNAUCdb_HfHM24m9-D8_R77t9DNrMPgisPtOonkqORJzTtHsz-FjyxXAHbp72urUGrHLLlZnRRsrpj4NFdLX_DuXwp5k3SiUlnWTLHk=w2214-h1312-no
[All devices model] https://lh3.googleusercontent.com/lRHdw7nVVz05dG57znq_Vzwwc9zFgEmeS_kQsrqcY2u6OOkcknrpMSytte14jOqmRH_829RAitVc3HVQL2St3g8iSh7STm9XNxuBTJkxAo9OPjbv6l02s02jT35IXWo2qhGy0hiiD4uBfAlYdyy-fjXGCAe4wMLvJYPiDUc9mY73Tf3rxRw5TJ1-XW11hyE3oeHj5OkMzET7_mMoKhro3Bs97HzdDOWO2_5QHAYeqwIuQNJ8NfjU1HTeozdOF_NuAk2nZF8YajHX_VJ4FZtOhovTltZGWJseQ2RzM3gsmd2nt5-obE3msOxPYXf3R9lYlolryRNpBFGHWOrR9xj-6QfuxIOHJOWhQAkkNEAOB7emcvvFzgSQkj0Xkoro2nZCkPH-wagSAJFm-cMhOjapdwhvvlUEjaOh9dw29RBT7jfYH5r2f7i2KELBxksG1uIVo9PcHjg0hvgC0xsw2WMLWFlDnWetngJxmyl6U8pM8NNLghm5CxFaMAKnygnN_4F0aCjfQEeBX-XaC5ss4_1fDQnifw7UcnhBH7n5VZqyYiK1jNqPYDGFJS1J8FJPkXmZsc8uOtAXIa0t58ZOrQ5jHeAKsIlrlj-eM_HP7hM9iQi0VCxjaRmonD_nc6ueKGjyj4xDm9b4uAXnO1B3krVfGAstaJ_RvHw=w552-h652-no
[updated devices model] https://lh3.googleusercontent.com/8uIPKFBfwEfGg7t_KlIB8xJrI1RPJW8WhozKIa7enNlGf7AMZqF0r8qz3FPZ6IzSlyfI3PFk6OFIskPpxzep5_cHudRK5LhpbHdsZkplM8aCoeOSO7-DG8bgTQ__-iEzpmhSG4HORmLauA80L6tEh3SJxbOPoKlconsj7cXWT2uZcPD9xYAjBYHtMp7wrOZXEeAHJPqLoonxIVPsQ-CGTmhVDjQW33YUqhUytDY1FlJcnNCRQVUTRsw5NasxwvCkDVdhFBFlDNyVXX6HN_GL-ZBLNmxrOQyxz_Q7_D7e_sGVp9iw0iQIABr4ogk-9chsY8F0iNjdrD67BxGXaExRN9Nog-TuHgDEf1uLgHUJi6TZRyoalalF-q1Rf6h3Xj3mKj56bSfgk8Z25W3fToOQBMwNEwcYIy8c4bzDjMzvS8san5XkRDIrilN7-iRwJQjH-Q43cgLeSg7wTBCFrqeRPblRCe79A1dVTEnlkHl9aPmp8aW9ih0b5yMcmJjK40BwqQu0sBplzBL_oKDeooVxQ7Yp-sAfa9XKHB-s4WiKbU5fAfURt_jsRO5JtHCeiyjVw9D2RiCZ_fbzTTZGMv4ysJNIYU9oADJLdl-zHTbGzpNggd-KxOWnoDffdHlQkH5nB_FeH11hMbkvdjpFarblpMjh_hhh5dM=w681-h763-no
Json where I'm getting Object
[{"id":"68","title":"testRecall","items":[{"id":992,"alarm":0,"name":"CloudMM","online":"offline","time":"2018-10-28 23:30:58","timestamp":1540806358,"acktimestamp":1540806358,"lat":0,"lng":0,"course":0,"speed":0,"altitude":0,"icon_type":"icon","icon_color":"red","icon_colors":{"moving":"green","stopped":"red","offline":"red","engine":"yellow"},"icon":{"id":10,"user_id":null,"type":"icon","order":3,"width":46,"height":64,"path":"images\/device_icons\/v2\/objects2a_38.png","by_status":"0"},"power":"-","address":"-","protocol":"osmand","driver":"-","driver_data":{"id":null,"user_id":null,"device_id":null,"name":null,"rfid":null,"phone":null,"email":null,"description":null,"created_at":null,"updated_at":null},"sensors":[],"services":[],"tail":[],"distance_unit_hour":"kph","unit_of_distance":"km","unit_of_altitude":"mt","unit_of_capacity":"lt","stop_duration":"0h","moved_timestamp":0,"engine_status":null,"detect_engine":"gps","engine_hours":"gps","total_distance":0,"device_data":{"id":992,"user_id":70,"current_driver_id":null,"timezone_id":null,"traccar_device_id":992,"icon_id":10,"icon_colors":{"moving":"green","stopped":"red","offline":"red","engine":"yellow"},"active":1,"deleted":0,"name":"CloudMM","imei":"665544332211","fuel_measurement_id":1,"fuel_quantity":"0.00","fuel_price":"0.00","fuel_per_km":"0.00","sim_number":"","device_model":"","plate_number":"","vin":"","registration_number":"","object_owner":"","additional_notes":"","expiration_date":null,"sim_expiration_date":"0000-00-00","sim_activation_date":"0000-00-00","installation_date":"0000-00-00","tail_color":"#33cc33","tail_length":5,"engine_hours":"gps","detect_engine":"gps","min_moving_speed":6,"min_fuel_fillings":10,"min_fuel_thefts":10,"snap_to_road":0,"gprs_templates_only":0,"valid_by_avg_speed":"1","parameters":"[\"batterylevel\",\"satellites\",\"hdop\",\"sequence\",\"distance\",\"totaldistance\",\"motion\",\"valid\",\"enginehours\"]","currents":null,"created_at":"2018-10-29 05:34:27","updated_at":"2019-02-27 07:58:31","forward":{"active":"1","ip":"198.121.31.32","port":"6000","protocol":"TCP"},"stop_duration":"0h","pivot":{"user_id":70,"device_id":992,"group_id":68,"current_driver_id":null,"active":1,"timezone_id":null},"traccar":{"id":"992","name":"CloudMM","uniqueId":"665544332211","latestPosition_id":"1","lastValidLatitude":null,"lastValidLongitude":null,"other":"<info><batterylevel>23<\/batterylevel><satellites>0<\/satellites><hdop>0<\/hdop><sequence>6<\/sequence><distance>0<\/distance><totaldistance>0<\/totaldistance><motion>false<\/motion><valid>false<\/valid><enginehours>0<\/enginehours><\/info>","speed":"0.00","time":"2018-10-29 09:45:45","device_time":"2018-10-29 09:45:45","server_time":"2018-10-29 09:45:58","ack_time":"2018-10-29 09:45:58","altitude":null,"course":null,"power":null,"address":null,"protocol":"osmand","latest_positions":null,"moved_at":null},"icon":{"id":10,"user_id":null,"type":"icon","order":3,"width":46,"height":64,"path":"images\/device_icons\/v2\/objects2a_38.png","by_status":"0"},"sensors":[],"services":[],"driver":null,"users":[{"id":70,"email":"tomas#gpswox.com"}],"lastValidLatitude":0,"lastValidLongitude":0,"latest_positions":null,"icon_type":"icon","group_id":68,"user_timezone_id":null,"time":"2018-10-29 09:45:45","course":0,"speed":0}},{"id":1099,"alarm":0,"name":"testRecalObject","online":"offline","time":"Not connected","timestamp":0,"acktimestamp":0,"lat":0,"lng":0,"course":0,"speed":0,"altitude":0,"icon_type":"arrow","icon_color":"red","icon_colors":{"moving":"green","stopped":"yellow","offline":"red","engine":"yellow"},"icon":{"id":0,"user_id":null,"type":"arrow","order":1,"width":25,"height":33,"path":"assets\/images\/arrow-ack.png","by_status":"0"},"power":"-","address":"-","protocol":"-","driver":"-","driver_data":{"id":null,"user_id":null,"device_id":null,"name":null,"rfid":null,"phone":null,"email":null,"description":null,"created_at":null,"updated_at":null},"sensors":[],"services":[],"tail":[],"distance_unit_hour":"kph","unit_of_distance":"km","unit_of_altitude":"mt","unit_of_capacity":"lt","stop_duration":"0h","moved_timestamp":0,"engine_status":null,"detect_engine":"gps","engine_hours":"gps","total_distance":0,"device_data":{"id":1099,"user_id":70,"current_driver_id":null,"timezone_id":null,"traccar_device_id":1099,"icon_id":0,"icon_colors":{"moving":"green","stopped":"yellow","offline":"red","engine":"yellow"},"active":1,"deleted":0,"name":"testRecalObject","imei":"oooopaaa","fuel_measurement_id":1,"fuel_quantity":"0.00","fuel_price":"0.00","fuel_per_km":"0.00","sim_number":"","device_model":"","plate_number":"","vin":"","registration_number":"","object_owner":"","additional_notes":"","expiration_date":null,"sim_expiration_date":"0000-00-00","sim_activation_date":"0000-00-00","installation_date":"0000-00-00","tail_color":"#63f542","tail_length":0,"engine_hours":"gps","detect_engine":"gps","min_moving_speed":1,"min_fuel_fillings":1,"min_fuel_thefts":1,"snap_to_road":0,"gprs_templates_only":0,"valid_by_avg_speed":"1","parameters":null,"currents":null,"created_at":"2019-07-20 21:46:11","updated_at":"2019-07-20 21:46:11","forward":null,"stop_duration":"0h","pivot":{"user_id":70,"device_id":1099,"group_id":68,"current_driver_id":null,"active":1,"timezone_id":null},"traccar":{"id":"1099","name":"testRecalObject","uniqueId":"oooopaaa","latestPosition_id":null,"lastValidLatitude":null,"lastValidLongitude":null,"other":null,"speed":null,"time":null,"device_time":null,"server_time":null,"ack_time":null,"altitude":null,"course":null,"power":null,"address":null,"protocol":null,"latest_positions":null,"moved_at":null},"icon":{"id":0,"user_id":null,"type":"arrow","order":1,"width":25,"height":33,"path":"assets\/images\/arrow-ack.png","by_status":"0"},"sensors":[],"services":[],"driver":null,"users":[{"id":70,"email":"tomas#gpswox.com"}],"lastValidLatitude":0,"lastValidLongitude":0,"latest_positions":null,"icon_type":"arrow","group_id":68,"user_timezone_id":null,"time":null,"course":0,"speed":0}}]}]
And where I'm getting String
{"items":[{"id":124,"alarm":0,"name":"Demo 1","online":"ack","time":"2019-08-22 03:26:02","timestamp":1566481263,"acktimestamp":0,"lat":55.675715,"lng":12.576404,"course":37,"speed":0,"altitude":1,"icon_type":"icon","icon_color":"yellow","icon_colors":{"moving":"green","stopped":"yellow","offline":"red","engine":"yellow"},"icon":{"id":3,"user_id":null,"type":"icon","order":3,"width":46,"height":64,"path":"images/device_icons/v2/objects2a_63_ack.png","by_status":"1"},"power":"-","address":"-","protocol":"homtecs","driver":"Testing","driver_data":{"id":"3","user_id":"1","device_id":"128","device_port":null,"name":"Testing","rfid":"ABC451132","phone":"4","email":"","description":"","created_at":"2017-06-20 19:00:43","updated_at":"2018-10-22 14:38:51"},"sensors":"[{\"id\":\"299\",\"type\":\"satellites\",\"name\":\"Satellites\",\"show_in_popup\":\"0\",\"value\":\"8\",\"val\":\"8\",\"scale_value\":null},{\"id\":\"300\",\"type\":\"satellites\",\"name\":\"Accuracy\",\"show_in_popup\":\"0\",\"value\":\"1.02\",\"val\":\"1.02\",\"scale_value\":null}]","services":"[{\"id\":\"9\",\"name\":\"Service one\",\"value\":\"Days Left (28d.)\",\"expiring\":false},{\"id\":\"19\",\"name\":\"Ignas\",\"value\":\"\\\"Sensor\\\" was not found.\",\"expiring\":true},{\"id\":\"20\",\"name\":\"Ignas w\",\"value\":\"Days Left (1560d.)\",\"expiring\":false},{\"id\":\"21\",\"name\":\"Poiu\",\"value\":\"\\\"Sensor\\\" was not found.\",\"expiring\":true}]","tail":"[{\"lat\":\"55.675024666667\",\"lng\":\"12.5744815\"},{\"lat\":\"55.675158166667\",\"lng\":\"12.574806833333\"},{\"lat\":\"55.675362333333\",\"lng\":\"12.575193666667\"},{\"lat\":\"55.675581666667\",\"lng\":\"12.575788833333\"},{\"lat\":\"55.675698666667\",\"lng\":\"12.5761065\"}]","distance_unit_hour":"kph","unit_of_distance":"km","unit_of_altitude":"mt","unit_of_capacity":"lt","stop_duration":"1min 42s","moved_timestamp":1566481160,"engine_status":null,"detect_engine":"gps","engine_hours":"gps","total_distance":7088342.97,"device_data":{"id":124,"user_id":70,"current_driver_id":3,"timezone_id":null,"traccar_device_id":124,"icon_id":3,"icon_colors":{"moving":"green","stopped":"yellow","offline":"red","engine":"yellow"},"active":1,"deleted":0,"name":"Demo 1","imei":"100000001","fuel_measurement_id":1,"fuel_quantity":"0.00","fuel_price":"0.00","fuel_per_km":"0.00","sim_number":"","device_model":"","plate_number":"","vin":"","registration_number":"","object_owner":"","additional_notes":"","expiration_date":null,"sim_expiration_date":"0000-00-00","sim_activation_date":"0000-00-00","installation_date":"0000-00-00","tail_color":"#33cc33","tail_length":5,"engine_hours":"gps","detect_engine":"gps","min_moving_speed":6,"min_fuel_fillings":10,"min_fuel_thefts":10,"snap_to_road":0,"gprs_templates_only":0,"valid_by_avg_speed":"1","parameters":"[\"sat\",\"hdop\",\"valid\",\"enginehours\",\"rfid\"]","currents":{"geofences":[]},"created_at":"2017-07-04 08:13:53","updated_at":"2018-12-20 08:10:59","forward":null,"stop_duration":"1min 42s","pivot":{"user_id":70,"device_id":124,"group_id":71,"current_driver_id":3,"active":1,"timezone_id":null},"traccar":{"id":"124","name":"Demo 1","uniqueId":"100000001","latestPosition_id":"9275412","lastValidLatitude":"55.675714833333","lastValidLongitude":"12.576404333333","other":"<info><sat>8</sat><hdop>1.02</hdop><valid>true</valid><enginehours>8862443</enginehours><distance>0.46</distance><totaldistance>7088342.97</totaldistance></info>","speed":"0.06","time":"2019-08-22 13:41:02","device_time":"2019-08-22 13:41:02","server_time":"2019-08-22 13:41:03","ack_time":null,"altitude":"1.4","course":"36.8","power":null,"address":null,"protocol":"homtecs","latest_positions":"55.675698666667/12.5761065;55.675581666667/12.575788833333;55.675362333333/12.575193666667;55.675158166667/12.574806833333;55.675024666667/12.5744815;55.6749245/12.574212666667;55.674809333333/12.5739365;55.674687833333/12.5736325;55.674487333333/12.5731665;55.674489666667/12.572678;55.67469/12.5724915;55.6748885/12.572210333333;55.675289/12.571647333333;55.675530666667/12.5713325;55.6757135/12.571215666667","moved_at":"2019-08-22 13:39:20"},"icon":{"id":3,"user_id":null,"type":"icon","order":3,"width":46,"height":64,"path":"images/device_icons/v2/objects2a_63_online.png","by_status":"1"},"sensors":[{"id":"299","user_id":"1","device_id":"124","name":"Satellites","type":"satellites","tag_name":"sat","add_to_history":"0","on_value":null,"off_value":null,"shown_value_by":null,"fuel_tank_name":null,"full_tank":null,"full_tank_value":null,"min_value":null,"max_value":null,"formula":null,"odometer_value_by":null,"odometer_value":null,"odometer_value_unit":"km","temperature_max":null,"temperature_max_value":null,"temperature_min":null,"temperature_min_value":null,"value":"10","value_formula":"0","show_in_popup":"0","unit_of_measurement":"","on_tag_value":null,"off_tag_value":null,"on_type":null,"off_type":null,"calibrations":null,"skip_calibration":null},{"id":"300","user_id":"1","device_id":"124","name":"Accuracy","type":"satellites","tag_name":"hdop","add_to_history":"0","on_value":null,"off_value":null,"shown_value_by":null,"fuel_tank_name":null,"full_tank":null,"full_tank_value":null,"min_value":null,"max_value":null,"formula":null,"odometer_value_by":null,"odometer_value":null,"odometer_value_unit":"km","temperature_max":null,"temperature_max_value":null,"temperature_min":null,"temperature_min_value":null,"value":"0.87","value_formula":"0","show_in_popup":"0","unit_of_measurement":"","on_tag_value":null,"off_tag_value":null,"on_type":null,"off_type":null,"calibrations":null,"skip_calibration":null}],"services":[{"id":"9","user_id":"1","device_id":"124","name":"Service one","expiration_by":"days","interval":"30","last_service":"2019-08-20","trigger_event_left":"5","renew_after_expiration":"1","expires":"0","expires_date":"2019-09-19","remind":"0","remind_date":"2019-09-14","event_sent":"0","expired":"0","email":"","mobile_phone":""},{"id":"19","user_id":"1","device_id":"124","name":"Ignas","expiration_by":"odometer","interval":"123456","last_service":"0","trigger_event_left":"0","renew_after_expiration":"0","expires":"123456","expires_date":null,"remind":"123456","remind_date":null,"event_sent":"0","expired":"0","email":"","mobile_phone":""},{"id":"20","user_id":"1","device_id":"124","name":"Ignas w","expiration_by":"days","interval":"2000","last_service":null,"trigger_event_left":"0","renew_after_expiration":"0","expires":"0","expires_date":"2023-11-29","remind":"0","remind_date":"1970-01-01","event_sent":"1","expired":"0","email":"","mobile_phone":""},{"id":"21","user_id":"1","device_id":"124","name":"Poiu","expiration_by":"odometer","interval":"12","last_service":"0","trigger_event_left":"0","renew_after_expiration":"0","expires":"12","expires_date":null,"remind":"12","remind_date":null,"event_sent":"0","expired":"0","email":"","mobile_phone":""}],"driver":{"id":"3","user_id":"1","device_id":"128","device_port":null,"name":"Testing","rfid":"ABC451132","phone":"4","email":"","description":"","created_at":"2017-06-20 19:00:43","updated_at":"2018-10-22 14:38:51"},"lastValidLatitude":55.675715,"lastValidLongitude":12.576404,"latest_positions":"55.675698666667/12.5761065;55.675581666667/12.575788833333;55.675362333333/12.575193666667;55.675158166667/12.574806833333;55.675024666667/12.5744815;55.6749245/12.574212666667;55.674809333333/12.5739365;55.674687833333/12.5736325;55.674487333333/12.5731665;55.674489666667/12.572678;55.67469/12.5724915;55.6748885/12.572210333333;55.675289/12.571647333333;55.675530666667/12.5713325;55.6757135/12.571215666667","icon_type":"icon","group_id":71,"user_timezone_id":null,"time":"2019-08-22 13:41:02","course":37,"speed":0}}],"events":[],"time":1566481263,"version":"3.4.3.1"}
Here is just proof-of-concept but I think that you'll get the idea.
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
data class Test(val sensors: List<Sensor>)
data class Sensor(val id: Int, val name: String)
class TestDeserializer : JsonDeserializer<Test> {
override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext): Test {
val jsonObject = json.asJsonObject
val sensorsJson = jsonObject["sensors"]
val sensors = if (sensorsJson.isJsonArray) {
sensorsJson.asJsonArray.map { context.deserialize<Sensor>(it, Sensor::class.java) }
} else {
val typeToken = TypeToken.getParameterized(List::class.java, Sensor::class.java)
val sensorsElement = JsonParser().parse(sensorsJson.asString)
context.deserialize(sensorsElement, typeToken.type)
}
return Test(sensors)
}
}
fun main() {
val gson = GsonBuilder()
.registerTypeAdapter(Test::class.java, TestDeserializer())
.create()
val jsonWithObjects = """
{
"sensors": [
{
"id": 1,
"name": "test 1"
},
{
"id": 2,
"name": "test 2"
},
{
"id": 3,
"name": "test 3"
}
]
}
""".trimIndent()
val test1 = gson.fromJson(jsonWithObjects, Test::class.java)
val jsonWithStrings = """
{
"sensors": "[{\"id\": 1,\"name\": \"test 1\"},{\"id\": 2,\"name\": \"test 2\"},{\"id\": 3,\"name\": \"test 3\"}]"
}
""".trimIndent()
val test2 = gson.fromJson(jsonWithStrings, Test::class.java)
println(test1 == test2) // prints true
}

How to get data from JsonObject and set as Array on RecyclerView in Kotlin?

I'm working on a project that was previously done by another developer. I continue to call API for the project. I've followed his way. But, I get stuck when I want to retrieve JsonArray and show it into RecyclerView.
How I can to get the data from JSONArray.
Interface:
interface TripService {
#FormUrlEncoded
#POST("driver/getAvailableTripList")
fun availableTripList(#Field("page") page : String): Call<JsonObject>
}
TripServiceHandler:
class TripServiceHandler {
private var instance: TripService? = null
// Singleton instance to access through the application
init {
if(instance == null) {
instance = TripFactory().createJourney(TripService::class.java)
}
}
// Method for Driver Balance Service
fun availableTripList(page: String, listener: ServerCallbackListener) =
this.instance?.let {this.instance!!.availableTripList(page).enqueue(RetrofitCallBackHandler.getHandler(listener))
}
Fragment:
private fun getAvailableTrip(){
showLoading()
TripServiceHandler().availableTripList("1", availableTripHandler)
}
private var availableTripHandler: ServerCallbackListener = object : ServerCallbackListener {
override fun onSuccess(baseResponse: JsonObject?) {
hideLoading()
val data = baseResponse!!.getAsJsonObject(AppConstants.KEY_DATA)
}
override fun onFailure(message: String?) {
showMessageBar(message, SnackBarUtility.MessageType.ERROR)
}
}
Data Model:
class Trip : Serializable {
#SerializedName("tid")
var tripId: String? = null
#SerializedName("max_passengers")
var maxPass: String? = null
#SerializedName("driver_revenue")
var priceTrip: String? = null
#SerializedName("origin_coordinate")
var originCoordinate: List<Coordinate>? = null
#SerializedName("destination_coordinate")
var destCoordinate: List<Coordinate>? = null
}
the jsonArray
"rows": [
{
"tid": "44551",
"did": null,
"status": 1,
"leaving_time": "1547093186",
"max_passengers": 12,
"total_revenue": 0,
"driver_revenue": 0,
"origin_coordinate": {
"x": 1.43762623,
"y": 103.80153311
},
"destination_coordinate": {
"x": 1.29481854,
"y": 103.78735487
},
"total_requests": 12,
"destination_geom": "0101000020E610000048F0284063F25940854F5F1901BFF43F",
"origin_geom": "0101000020E61000002AB6CC40C7F25940032A19ECF7E4F63F",
"stops": [
{
"sid": "46969",
"count": "4",
"order_seq": 1,
"mode": 0,
"name": "Woodlands Ave 4",
"description": "Blk 532",
"coordinate": {
"x": 1.43059181782,
"y": 103.792699721
},
"eta_time": "1547093186",
"leaving_time": "1547093366"
},
As I have seen, your data should be a JSONobject type value already and since you are using serialized name i assume you are using Gson library for it.
First, make another model beforehand to contain your List (since it passed in rows)
data class TripList:Serializeable{
#("rows")
myList:List<Trip> = ArrayList()
}
Then, you can try this
val tripList = gson.fromJson(data, TripList::class.java)
Your tripList will then be TripList type of class, access your list with tripList.myList. Hope it will work for your solution

Categories

Resources