Error in parsing json with class Kotlin Android - android

hi i am trying to parse JSON with kotlin
below is my json code
[{
"module":"1",
"books":[{"name":"bookname1","authors":"author1, author 2"},
{"name":"bookname2","authors":"author1, author 2"},
{"name":"bookname3","authors":"author1, author 2"}]
},
{
"module":"2",
"books":[{"name":"bookname1","authors":"author1, author 2"},
{"name":"bookname2","authors":"author1, author 2"},
{"name":"bookname3","authors":"author1, author 2"}]
},
{
"module":"3",
"books":[{"name":"bookname1","authors":"author1, author 2"},
{"name":"bookname2","authors":"author1, author 2"},
{"name":"bookname3","authors":"author1, author 2"}]
},
{
"module":"4",
"books":[{"name":"bookname1","authors":"author1, author 2"},
{"name":"bookname2","authors":"author1, author 2"},
{"name":"bookname3","authors":"author1, author 2"}]
},
{
"module":"5",
"books":[{"name":"bookname1","authors":"author1, author 2"},
{"name":"bookname2","authors":"author1, author 2"},
{"name":"bookname3","authors":"author1, author 2"}]
}]
please note that this json response starts with array
here is my class to parse it
class SemdetailsPArser {
#SerializedName("module")
#Expose
var module: String? = null
#SerializedName("books")
#Expose
var books: List<Book>? = null
}
class Book {
#SerializedName("name")
#Expose
var name: String? = null
#SerializedName("authors")
#Expose
var authors: String? = null
}
And here is my code
//interface
interface SemdetailsFetcher {
#GET("test/json/sub1.json")
fun getCurrentSemData(): Call<SemdetailsPArser>
}
here is my code in activity
fun getCurrentData() {
val retrofit = Retrofit.Builder()
.baseUrl(BaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(SemdetailsFetcher::class.java)
val call = service.getCurrentSemData()
call.enqueue(object : Callback, retrofit2.Callback<SemdetailsPArser> {
override fun onResponse(
call: retrofit2.Call<SemdetailsPArser>?,
response: retrofit2.Response<SemdetailsPArser>?
) {
// val thisthig = response?.body();
println("here 1 ${response?.body().toString()}")
}
override fun onFailure(call: Call?, e: IOException?) {
println("here 2")
}
override fun onFailure(call: retrofit2.Call<SemdetailsPArser>?, t: Throwable?) {
println("here 3 $t")
}
override fun onResponse(call: Call, response: Response) {
if (response.code() == 200) {
println("secodn success")
val sampleResp = response.body()!!
println(sampleResp)
}
}
})
}
and i am getting this error
here 3 com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $
I understood that this might be related to my parsing class
here i am getting an array of json info, i tried the same code with another json
{
"person": {
"name": "Don",
"age": 35
},
"books": [{
"id": 800,
"name": "book 1",
"description": "clear sky",
"icon": "01n"
},
{
"id": 801,
"name": "book 2",
"description": "clear sky 1",
"icon": "01N"
}
],
"city": "bgvnslsl",
"id": 1851632,
"bname": "abcd",
"code": 200
}
this was working perfectly when i changed the parsing class and interface
My problem is that i dont know how to write a class to parse a json response starting with an array

You are expecting list of SemdetailsPArser , so you should define return type as List of SemdetailsPArser
This should fix problem.
interface SemdetailsFetcher {
#GET("test/json/sub1.json")
fun getCurrentSemData(): Call<List<SemdetailsPArser>>
}
You also need to change it in other parts of code.

The error you are getting means that you're trying to parse JSON array, thinking it should be JSON object. JSON array is the thing between these [], while JSON object is in curly brackets like these {}. So your first JSON corresponds to something like List<Module>, it's not an object, but a list of them. Each module has a list of books in it.
So all said, it should be like this
interface SemdetailsFetcher {
#GET("test/json/sub1.json")
fun getCurrentSemData(): Call<List<SemdetailsPArser>>
}
By the way, if you define your POJOs right, you won't need all the annotations.

Create SemdetailsPArser class
data class SemdetailsPArser(
val books: List<Book>,
val module: String
)
Next create Book class
data class Book(
val authors: String,
val name: String
)
next in the interface (SemdetailsFetcher)
interface SemdetailsFetcher {
#GET("test/json/sub1.json")
fun getCurrentSemData(): Call<List<SemdetailsPArser>>
}

Related

Android parse JSON using JacksonAnnotation

I'm trying to parse a JSON without results.
I've a response like following:
{
"0": {
"id": "4",
"nome": "Zero Gravity",
"id_stop": "0"
},
"1": {
"id": "540",
"nome": "First Name",
"id_stop": "111"
}
}
The problem is that it's not a list with a defined id, but the id is a progressive integer that varies according to the possible answers. How do I do proper parsing? I tried building a response like this:
here my service
#GET("{path}")
suspend fun getResponse(
#Path(value = "path", encoded = true) path: String,
#Query(value = "cmd") alias: String? = "cmd",
#Query(value = "id_where") idWhere: String,
): Response<ArrayList<APIResponse>>
Here APIResponse
data class APIResponse(
#JsonProperty("id")
val id: String?,
#JsonProperty("nome")
val nome: String?,
#JsonProperty("id_stop")
val idStop: String?,
) {
fun toDomain(): API {
return API( id, nome, idStop
)
}
}
here my repository
suspend fun getAPIResponse(from: String) : API {
val response = service.getResponse(path = basePath, idWhere = where)
return response.body()?.map {
it.toDomain()
}
}
This isn't the solution though, because I can't get a complete answer but I always have a single item with all fields null. Should I use a HashMap? how could i solve?

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.

android-kotlin JsonObject JsonArray send request data via POST

I want to send data request via post to server I want to know How can I add data in array
data class City(
#SerializedName("cityId")
val cityId: Int?,
#SerializedName("detail")
val detail: List<String?>
)
Request
data class CityRequest(
#SerializedName("listCity")
val listCity: List<City?>
)
Response
data class CityResponse(
#SerializedName("code")
val code: String?,
#SerializedName("status")
val status: Boolean?,
#SerializedName("message")
val message: String?
)
API Server
#Headers("Content-Type: application/json")
#POST("city")
suspend fun sendCityContent(#Body listCity: CityRequest?):
Call<CityResponse?>
Connect Service
I don't know how I can add information to this section in question.
private suspend fun sendDataCity(city: List<city?>) {
val retrofit = clientCity
val sendDataToServer = retrofit?.create(CityService::class.java)
val call = sendDataToServer?.sendCityContent(CityRequest(city))
call?.enqueue(object : Callback<CityResponse?> {
override fun onResponse(
call: Call<CityResponse?>, response: Response<CityResponse?>) {
val getResponse = response.body()
Timber.tag("SALE_CITY: ").d("code: %s", getResponse?.code)
Timber.tag("SALE_CITY: ").d("status: %s", getResponse?.status)
Timber.tag("SALE_CITY: ").d("message: %s", getResponse?.message)
}
override fun onFailure(call: Call<CityResponse?>, t: Throwable?) {
t?.printStackTrace()
}
})
}
JSON Simple
{
"city": [
{
"cityId": 1,
"answer": [
"1"
]
},
{
"questionId": 2,
"answer": [
"2.1",
"2.2"
]
}
]}
What do I have to do next?
Can you have a sample add data in array for me?
Things I want
cityId = 1
detail = "1.1", "1.2"
cityId = 2
detail = "2.1", "2.2"
thank you
One issue i can see with your request is the key is different from what you are sending might be different check that. it should be city not listCity as given.
data class CityRequest(
#SerializedName("city")
val city: List<City?>
)
and your city class should have these keys answer which you have mentioned as details
data class City(
#SerializedName("cityId")
val cityId: Int?,
#SerializedName("answer")
val answer: List<String?>
)
I guess you are just sending with wrong keys that might be the reason the server is not accepting the request. make the above change it should work post if you get error.

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
}

Kotlin Json Question Expected a string but was BEGIN_OBJECT at path

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.

Categories

Resources