Hi I'm trying to deserialize json to a sealed clas...
# serialization
r
Hi I'm trying to deserialize json to a sealed class, but it fails with a decoding exception that says
Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Expected JsonPrimitive at self, found {"href":"<https://horizon.stellar.org/operations/122511124621283329>"}
. Could anyone take a look and tell me if I'm missing something obvious?. I'll put the code in the replies.
Copy code
{
  "_links": {
    "self": {
      "href": "<https://horizon.stellar.org/operations/122511124621283329>"
    },
    "transaction": {
      "href": "<https://horizon.stellar.org/transactions/452a180790caf4dbe658d996316cd727ce5573f5f0a77790da540cc49214fe80>"
    },
    "effects": {
      "href": "<https://horizon.stellar.org/operations/122511124621283329/effects>"
    },
    "succeeds": {
      "href": "<https://horizon.stellar.org/effects?order=desc>&cursor=122511124621283329"
    },
    "precedes": {
      "href": "<https://horizon.stellar.org/effects?order=asc>&cursor=122511124621283329"
    }
  },
  "id": "122511124621283329",
  "paging_token": "122511124621283329",
  "transaction_successful": true,
  "source_account": "GCAXBKU3AKYJPLQ6PEJ6L47KOATCYCBJ2NFRGAK7FUUA2DCEUC265SU2",
  "type": "payment",
  "type_i": 1,
  "created_at": "2020-03-04T22:46:47Z",
  "transaction_hash": "452a180790caf4dbe658d996316cd727ce5573f5f0a77790da540cc49214fe80",
  "asset_type": "credit_alphanum4",
  "asset_code": "NGNT",
  "asset_issuer": "GAWODAROMJ33V5YDFY3NPYTHVYQG7MJXVJ2ND3AOGIHYRWINES6ACCPD",
  "from": "GCAXBKU3AKYJPLQ6PEJ6L47KOATCYCBJ2NFRGAK7FUUA2DCEUC265SU2",
  "to": "GC2QCKFI3DOBEYVBONPVNA2PMLU225IKKI6XPENMWR2CTWSFBAOU7T34",
  "amount": "5.0000000"
}
Copy code
@Serializable
@JsonClassDiscriminator("type")
sealed class OperationResponse(){
    abstract val id: String //long?
    @SerialName("paging_token") abstract val pagingToken: String
    @SerialName("transaction_hash") abstract val transactionHash: String
    @SerialName("transaction_successful") abstract val transactionSuccessful: Boolean
    @SerialName("source_account") abstract val sourceAccount: String
    @SerialName("created_at") abstract val createdAt: String
    @SerialName("type_i") abstract val typeI: Int
    @SerialName("type") abstract val type: String
    @SerialName("_links") abstract val links : Links
}
Copy code
@Serializable
@SerialName("payment1")
data class PaymentResponse(
    override val id : String, //long?
    @SerialName("paging_token") override val pagingToken : String,
    @SerialName("transaction_hash") override val transactionHash : String,
    @SerialName("transaction_successful") override val transactionSuccessful : Boolean,
    @SerialName("source_account") override val sourceAccount : String,
    @SerialName("created_at") override val createdAt : String,
    @SerialName("type_i") override val typeI: Int,
    @SerialName("type") override val type: String,
    @SerialName("asset_type") val assetType: String,
    @SerialName("asset_code") val assetCode: String,
    @SerialName("asset_issuer") val assetIssuer: String,
    val from: String,
    val to: String,
    val amount: String,
    @SerialName("_links") override val links : Links,
) : OperationResponse()
Copy code
@Serializable
data class Links(
    @Serializable(with = HrefSerializer::class)
    val self: String,
    @Serializable(with = HrefSerializer::class)
    val transaction: String,
    @Serializable(with = HrefSerializer::class)
    val effects: String,
    @Serializable(with = HrefSerializer::class)
    val succeeds: String?,
    @Serializable(with = HrefSerializer::class)
    val precedes: String?
)
Copy code
class HrefSerializer : KSerializer<String> {

    override val descriptor: SerialDescriptor =
        buildClassSerialDescriptor("hrefobj") {
            element("href", PrimitiveSerialDescriptor("href", PrimitiveKind.STRING))
        }

    override fun deserialize(decoder: Decoder): String =
        decoder.decodeStructure(descriptor) {
            var href = ""
            while (true) {
                when (decodeElementIndex(descriptor)) {
                    0 -> href = decoder.decodeString()
                    DECODE_DONE -> break
                    else -> {
                        continue
                    }
                }
            }
            href
        }

    override fun serialize(encoder: Encoder, value: String) {
        TODO("Not yet implemented")
    }
}
If I try to deserialize it directly into
PaymentResponse
it works. But if I try to deserialize it into
OperationResponse
it fails with the error. Maybe the
HrefSerializer
is not being used somehow?
Copy code
@SerialName("payment1")
is a type, it should be "payment".
a
instead of writing a custom descriptor, make KxS generate one :)
r
Wow. That works. Thanks!
I'm still curious though, do you see anything wrong with the serializer I wrote? I was trying to figure it out just now and I realized that it works if I put
"type": "payment"
at the top of the json. I am confused lol
a
yeah I’m curious about it too
my guess is that there’s a bug in the
JsonClassDiscriminator
code. I suspect it’s not taking your custom-defined serializer into account, and expects the hrefs to be strings, which fails.. Your code will work if I decode the subtype, not the sealed class
Copy code
// fails
val operation = Json.decodeFromString<OperationResponse>(json)
// works
val payment = Json.decodeFromString<PaymentResponse>(json)
I have no idea why it’s respecting the plugin-generated one though….
I’ve stripped down the code, and I’ll make an issue