how to make list of objects from a fragment of json-file?

60 Views Asked by At

i want to use json file from the api-request to google sheets. from request i am getting such file:

{ "range": "'Пн'!A1:AB986", "majorDimension": "ROWS", "values": [ [ "Точка", "Отдать", "Забрать" ], [ "Заводы" ], [ "Пластик", "", "карт" ], [ "Балашов", "тов" ], [ "ПЭК", "", "док" ], [ "балашов" ], [ "таможня" ], [], [], [ "", "", "", "", " " ], [], [], [], [], [ "Центр" ], [ "ведомости", "тов1", "рем" ], [ "балашов" ], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [ "Московский" ], [ "таможня", "рем доки" ], [], [], [], [], [], [], [], [], [], [], [], [], [ "Прочее" ] ] }

(maybe it is more visible in this picture).

but for work i need only "values". for every array in "values" i want it to convert into Array of UserDataModel.

here is my code: UserDataModel.kt

data class UserDataModel(var point:String?, var get: String?, var take: String?, var isSelected: Boolean = false)

ActivityChoosing.kt (a fragment with function where i am making request)

private fun getDataFromAPI(){
        var url: String = "https://sheets.googleapis.com/v4/spreadsheets/10-yIy1ZuPSjqgmXd7kO9sHobuVlpZHQnz5dMrou0cVs/values/Пн?alt=json&key=${API}"
        val request = okhttp3.Request.Builder().url(url).build()
        val client = OkHttpClient()
            .newBuilder()
            .addInterceptor { chain ->
                val originalRequest = chain.request()
                val builder = originalRequest
                    .newBuilder()
                val newRequest = builder.build()
                chain.proceed(newRequest)
            }.build()
        client.newCall(request).enqueue(object : Callback{
            override fun onFailure(call: Call, e: IOException) {
                Log.d("Mylog", "failed")
                e.printStackTrace()
            }
            override fun onResponse(call: Call, response: okhttp3.Response) {
                DATA_FROM_SHEETS = response.body?.string()!!
                var json = JSONObject(DATA_FROM_SHEETS)
                val gson = Gson()
                val model_list = gson.fromJson(json["values"].toString() , Array<UserDataModel>::class.java)
               Log.d("Mylog", model_list.toString() )
            }
        })

        }

but when initializing model_list (string 4 in onResponse()) i am gettin "Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3 path $[0]". how can i do it correctly?

1

There are 1 best solutions below

0
AndrewL On BEST ANSWER

The serialization just doesn't work because you have an array, e.g. [ "Пластик", "", "карт" ] that doesn't serialize to the object UserDataModel because this is not array!

I'd approach this with an intermediate object to read easily from the JSON stream, and I wouldn't mix using JSONObject() and Gson.

import com.google.gson.Gson

data class UserDataModel(
    val point: String?,
    val get: String?,
    val take: String?,
    var isSelected: Boolean = false
)

data class ReaderModel(val values: List<List<String>>)

fun main(args: Array<String>) {
    val source = """
            { "range": "'Пн'!A1:AB986", "majorDimension": "ROWS", "values": [ [ "Точка", "Отдать", "Забрать" ], [ "Заводы" ], [ "Пластик", "", "карт" ], [ "Балашов", "тов" ], [ "ПЭК", "", "док" ], [ "балашов" ], [ "таможня" ], [], [], [ "", "", "", "", " " ], [], [], [], [], [ "Центр" ], [ "ведомости", "тов1", "рем" ], [ "балашов" ], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [ "Московский" ], [ "таможня", "рем доки" ], [], [], [], [], [], [], [], [], [], [], [], [], [ "Прочее" ] ] }
        """.trimIndent()

    // first read the JSON into this object which has a List of List of Strings
    // if you don't need the range and majorDimension fields, Gson will by default ignore them
    val parsed = Gson().fromJson(source, ReaderModel::class.java)
    println(parsed)

    // now using Kotlin transform that into the desired object
    val models : List<UserDataModel> = parsed.values
        .drop(1) // if the first row contains titles, this is how to ignore that first row
        .map {
            UserDataModel(
                point = if (it.size >= 1) it[0] else null,
                get = if (it.size >= 2) it[1] else null,
                take = if (it.size >= 3) it[2] else null,
            )
        }
        .filter { !it.point.isNullOrBlank() } // if you want it ignore the emtpy/blank nodes
    models.forEach { println(it) }
}