Home > Software design >  Can't decode the arraylist of objects from json string using serialization in Kotlin
Can't decode the arraylist of objects from json string using serialization in Kotlin

Time:10-07

So, I have ActorModel class that includes arraylist of MovieModel. I was trying to encode it and send it in a bundle to the next activity. However, it is saying:

kotlinx.serialization.json.internal.JsonDecodingException: Expected start of the object '{', but had 'EOF' instead at path: $
JSON input: kotlinx.serialization

Here is my ActorModel Class:

package com.sanjarbek.flixster2

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.Serializable


@Serializable
data class ActorModel(
    val name: String,
    val popularity: String,
    val profile_path: String, ) {

    var movieModelArrayList: ArrayList<MovieModel> = ArrayList<MovieModel>()
        set(value) {field = value}
        get() = field

    fun mainMovie(): String {
        val json = Json.encodeToString<ArrayList<MovieModel>>(movieModelArrayList)
        return json
    }
    fun main(): String {
        val json = Json.encodeToString(ActorModel(name, popularity, profile_path))
        return json
    }
    }

followed by MovieModel class:

package com.sanjarbek.flixster2

import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.Serializable

@Serializable
data class MovieModel (
    private val original_title: String="",
    private val poster_path: String="",
    private val overview: String=""
) {

    fun get_title(): String{
        return original_title
     }

    fun get_image(): String{
        return poster_path
    }

    fun get_description(): String{
        return overview
    }

}

Here's the code where I tried sending it with bundle:

holder.image.setOnClickListener {
        val intent = Intent(context, ActorInfoActivity::class.java)
        intent.putExtra("image", image_url)
        intent.putExtra("name", model.name)
        intent.putExtra("model", model.main())
        context.startActivity(intent)
    }

And where I receive and decode it:

val actor = Json.decodeFromString<ActorModel>("kotlinx.serialization")
val movies = Json.decodeFromString<ArrayList<MovieModel>>("kotlinx.serialization")
Log.d("TAG", "onCreate: $actor"
Log.d("TAG", "onCreate: $movies")

CodePudding user response:

If your question is how to pass the objects described above to another activity, then this does not require a JSON object. Android gives us the tools to do this out of the box. I rewrote your classes a bit:

ActorModel

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class ActorModel (
    val name: String,
    val popularity: String,
    val profilePath: String,
    var movieModelArrayList: MutableList<MovieModel> = mutableListOf()): Parcelable {

    override fun toString(): String {
        return "ActorModel(name=$name, popularity=$popularity, profilePath=$profilePath)\n"  
                "movieModelArrayList=$movieModelArrayList"
    }
}

MovieModel

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
data class MovieModel (
    val original_title: String = "TestTitle",
    val poster_path: String = "./Test",
    val overview: String = "Test"
): Parcelable

I draw your attention to the @Parcelize annotation and interface Parcelable - this is the most important change to your code. Parcelable process is much faster than Serializable. One of the reasons for this is that we are being explicit about the serialization process instead of using reflection to infer it. In fact, we must manually implement the methods of the Parcelable interface. But the creators of Kotlin took care of us and added the @Parcelize annotation, which does all the dirty work for us. By default, it is not available, how to enable it is described in detail in the answer.

Sending data

import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class TestActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val actor = ActorModel("Alex", "10", "./profiles")
        actor.movieModelArrayList.add(MovieModel())

        val intent = Intent(this, MainActivity::class.java)
        val bundle = Bundle()
        bundle.putParcelable("actor", actor)
        intent.putExtras(bundle)

        startActivity(intent)
    }
}

Getting data

class MainActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val actor = intent.extras?.get("actor") as ActorModel
        Log.i("Test", actor)
    }
}

A small digression

In the MovieModel class, you do not need getters, since you have already declared the fields as val - they are no longer mutable and read-only. So, the private modifier also makes no sense. It's not Java anymore, relax and enjoy Kotlin :)

  • Related