Skip to content

JsonAlias fails with Kotlin 1.8.20 #624

Closed
@efemoney

Description

@efemoney

Describe the bug
We have a json parsing test that is failing with 1.8.20-Beta but passes with 1.8.10.

To Reproduce
More info below but you can probably reproduce by running kotlin 1820-Beta and deserialising a class using @JsonAlias

Expected behavior
It should not fail.

Versions
Kotlin: 1.8.20-Beta
Jackson-module-kotlin: 2.14.2
Jackson-databind: 2.14.2

Additional context
We have a custom value class (de)serializer to "transparently" deserialise ie it will deserialize the 'wrapped' object then reflectively call the value class constructor . The 'wrapped' model in this particular test however has @JsonAlias annotation on a field.

When we test with the actual field name amount, the test passes. When the json uses the alias instead, it fails.

Our value class deserializer:

private class ValueClassDeserializer<T : Any>(kls: KClass<T>) :
  StdDelegatingDeserializer<T>(ValueConstructorConverter(kls)) {
  override fun withDelegate(
    converter: Converter<Any, T>,
    delegateType: JavaType,
    delegateDeserializer: JsonDeserializer<*>,
  ) = StdDelegatingDeserializer(converter, delegateType, delegateDeserializer)
}


private class ValueConstructorConverter<T : Any>(private val kls: KClass<T>) : Converter<Any?, T> {
  private val ctor = kls.primaryConstructor!!
  private val paramType = ctor.parameters.single().type.javaType
  override fun convert(value: Any?): T = ctor.call(value)
  override fun getInputType(typeFactory: TypeFactory): JavaType = typeFactory.constructType(paramType)
  override fun getOutputType(typeFactory: TypeFactory): JavaType = typeFactory.constructType(kls.java)
}

The models:

@JvmInline
value class AmountInCents(val value: BigInteger) : Comparable<AmountInCents> { ... }

class MoneyInCents(
  @field:JsonAlias("amountInCents") val amount: AmountInCents, 
  val currency: Currency,
) {
  companion object {
    fun ZeroIn(currency: Currency) = MoneyInCents(AmountInCents.Zero, currency)
  }
}

@JvmInline
value class Savings(val value: MoneyInCents) {
  inline val amount get() = value.amount
  inline val currency get() = value.currency
}

The test:

@Nested inner class SavingsTest(@Autowired private val mapper: JacksonTester<Savings>) {

    @Test
    fun testAmount() {
      val result = mapper.parse("""{"amount" : 1500, "currency": "AED"}""")
      assertNotNull(result.`object`) {
        assert(it.amount == AmountInCents(1500))
        assert(it.currency == Currency.AED)
      }
    }

    @Test
    fun testAmountInCents() {
      val result = mapper.parse("""{"amountInCents" : 1500, "currency": "AED"}""")
      assertNotNull(result.`object`) {
        assert(it.amount == AmountInCents(1500))
        assert(it.currency == Currency.AED)
      }
    }

    @Test
    fun testUnspecifiedCurrency() {
      val result = mapper.parse("""{"amountInCents" : "0", "currency": ""}""")
      assertNotNull(result.`object`) {
        assert(it.amount == AmountInCents.Zero)
        assert(it.currency == Currency.Unspecified)
      }
    }
  }

The last 2 tests, where the json key is the alias amountInCents, are now failing with 1.8.20-Beta vs 1.8.10.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions