Sundeep1501
Sundeep1501

Reputation: 1538

Kotlin data class | add my own variables other than JSON keys

I'm using retrofit and kotlin for my andorid app.

For one of my APIs, I have the following data classes

class Set(val set_id: Long, val tickets: List<Ticket>, val timer: Int) {}
class Ticket(val ticket_id: Long, val rows: List<Row>) {}
class Row(val row_id: Long, val row_numbers: List<Int>) {}

Sample JSON DATA

{
  "set_id": 60942,
  "tickets": [
    {
      "ticket_id": 304706,
      "rows": [
        {
          "row_id": 914116,
          "row_numbers": [
            0,
            11,
            21,
            0,
            42,
            52,
            0,
            76,
            85
          ]
        }
      ]
    }
  ],
  "timer": 12
}

Set class contains a list of Ticket and each ticket has a list of Row

My JSON object contains only these values and it is working fine till here. Retrofit mapping is also working.

Problem: I want to add my own a boolean field/variable isPlaying for class Ticket, which will be updated in the app later. But, this field should be set to true by default.

So I've tried this

class Ticket(val ticket_id: Long, val rows: List<Row>, var isPlaying: Boolean = true) {}

and this

class Ticket(val ticket_id: Long, val rows: List<Row>) {
    var isPlaying: Boolean = true
}

NOTE: JSON doesn't have isPlaying key, I want it for app logic only.

Both did not work, isPlaying is always showing false. I want it to be true by default.

Please, help me out. Thank you!

Upvotes: 9

Views: 4924

Answers (3)

Ilosqu
Ilosqu

Reputation: 336

Default arguments would not work in data class in the case when objects are instantiated with retrofit due to parsing library used underneath, which probably creates objects without calling constructor. For example Gson uses sun.misc.Unsafeto create objects.

What you can do - is to add backing property for fields that have default values:

class Ticket(
    val ticket_id: Long, 
    val rows: List<Row>, 
    private var _isPlaying: Boolean? = true
) {
    var isPlaying
        get() = _isPlaying ?: true
        set(value) {
            _isPlaying = value
        }
}

Upvotes: 4

hluhovskyi
hluhovskyi

Reputation: 10126

The root of the problem is the fact that GSON creates object not via constructor call, but via Unsafe staff. Thus it leads to broken things like null-safety or property initialization.

The only way I know how to workaround your case looks like this:

class Ticket(val ticket_id: Long, val rows: List<Row>) {
    private var _isPlaying: Boolean? = null
    var isPlaying: Boolean
        get() = _isPlaying ?: true // <-- default value
        set(value) {
            _isPlaying = value
        }
}

Here we create one backing property _isPlaying which will be initialized to null by GSON. Next we add custom getter and setter for public property which checks each it is invoked if _isPlaying is null. And if so it returns your default value. Otherwise, it returns the value stored in _isPlaying

Yes, it looks ugly. Otherwise you should consider using another json library like Jackson which as I know supports Kotlin well.

Upvotes: 1

Jarvis
Jarvis

Reputation: 1802

With using GsonBuilder setFeildNamePolicy

 val gson = GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            .create()


        val info = gson.fromJson(json, Account::class.java)


data class Account(
        @SerializedName("user_name") var userName: String = "",
        @SerializedName("ticket_id")  var ticketId: String = "",
        @SerializedName("row_id")  var rowId: String = "")

}

Upvotes: -1

Related Questions