Kotlinx.serialization: Computed property for serialization

Created on 3 Dec 2020  ·  8Comments  ·  Source: Kotlin/kotlinx.serialization

Hi there, Is there any way to make computed property for serialization?

The code down below produces a compilation error:
e: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Failed to generate expression: KtNameReferenceExpression

but I expected it works properly:

@Serializable
data class Record(val id: String,
                  val sourceUrl: String,
                  val index: Int = indexFromUri(Uri.parse(sourceUrl)),
                  val name: String = nameFromUri(Uri.parse(sourceUrl))) {
}


fun indexFromUri(uri: Uri): Int {
    val lastComponent = uri.lastPathSegment?.substringAfterLast('/')
    return lastComponent?.take(2)?.toIntOrNull() ?: 0
}

fun nameFromUri(uri: Uri): String {
    val lastComponent = uri.lastPathSegment?.substringAfterLast('/')
    return lastComponent?.substring(3) ?: "No name"
}

Kotlin: 1.4.20
kotlinx-serialization-json: 1.0.1

1 bug compiler-plugin design feature

All 8 comments

Properties with default values ​​that depend on other properties are not supported yet.

As a simple workaround, you can define hidden nullable fields special for serialization.
Something like:

@Serializable
data class Record(val id: String,
                  val sourceUrl: String,
                  private val index_: Int? = null,
                  private val name_: String? = null) {
    @Transient
    val index = index_ ?: indexFromUri(Uri.parse(sourceUrl))
    @Transient
    val name = name_ ?: nameFromUri(Uri.parse(sourceUrl))
} 

This solution causes unobvious behavior of equals/hashCode functions in some cases so you need to be alert.

Thank you for answer @shanshin
Awesome, but with your solution I lost ability to read fields index and name from JSON like regular
The idea is to compute default value in case of absence of value in JSON

If a value is absent in the JSON property name or index will have the default value.
e.g. for string {"id":"1","sourceUrl":"http://example.com/hello"} properties index and name take default values.

@shanshin Let me describe more carefully with an example

We have JSONs
{"id":"1","index":"12","name":"Hello","sourceUrl":"http://example.com/3463464"}
and
{"id":"1","sourceUrl":"http://example.com/12%20Hello"}

I expect both should give the same result in Record representation,
because in first case serializer should take values for name and index fields from JSON,
in second case serializer should take values from sourceUrl because fields are absent in JSON

P.S. Method of getting values from sourceUrl was mentioned before

Now it looks kind of impossible...

If the names of JSON attributes is fixed you can redefine the serial name of properties like this

@Serializable
data class Record(val id: String,
                  val sourceUrl: String,
                  @SerialName("index")
                  private val index_: Int? = null,
                  @SerialName("name")
                  private val name_: String? = null) {

    @Transient
    @SerialName("index_public")
    val index = index_ ?: indexFromUri(Uri.parse(sourceUrl))

    @Transient
    @SerialName("name_public")
    val name = name_ ?: nameFromUri(Uri.parse(sourceUrl))
} 

If you using equals/hashCode you should also override these functions to use index and name not index_, name_ properties, same with toString if necessary.

Another workaround is to create a custom KSerializer that will implement these intermediate computations.

Duplicate of #133

@shanshin Does it make sense to use @SerialName on a @Transient property?

@shanshin Does it make sense to use @SerialName on a @Transient property?

No. Because @Transient properties are not serialized SerialName does not affect encoding/decoding.
In the example above they are given as an example for clarity only.

Was this page helpful?
0 / 5 - 0 ratings