Skip to content

Fields starting with lowercase non ASCII characters are silently dropped when serializingΒ #1154

Closed

Description

@olesls

Search before asking

  • I searched in the issues and found nothing similar.
  • I have confirmed that the same problem is not reproduced if I exclude the KotlinModule.
  • I searched in the issues of databind and other modules used and found nothing similar.
  • I have confirmed that the problem does not reproduce in Java and only occurs when using Kotlin and KotlinModule.

Describe the bug

When a field name starts with a (lowercase) non ASCII character, it is silently dropped when serialized. This behaviour did not exist in Jackson2.

Example:

data class Foo(
    val ΓΈx: String = "bar",
)

Results in an empty JSON: {}

I think this happens because the field ends up with a lowercase character in the getter in Kotlin Bytecode.
E.g. val foo results in getFoo, but val ΓΈx results in getΓΈx.

To Reproduce

data class Foo(
    val ΓΈx: String = "disappears",
    val yΓΈ: String = "ok",
)

fun main() {
    val jackson2Mapper = com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
    val jackson2Json = jackson2Mapper.writeValueAsString(Foo())
    println("Jackson 2: $jackson2Json")

    val jackson3Mapper = tools.jackson.module.kotlin.jacksonMapperBuilder().build()
    val jackson3Json = jackson3Mapper.writeValueAsString(Foo())
    println("Jackson 3: $jackson3Json")

    // Output:
    // Jackson 2: {"ΓΈx":"disappears","yΓΈ":"ok"}
    // Jackson 3: {"yΓΈ":"ok"}
}

Expected behavior

I expected the field to be serialized like fields consisting of ASCII characters (like it did in Jackson2), or for an error to be thrown.

Versions

Kotlin: 2.3.20
Jackson-module-kotlin: 3.1.2

Additional context

@JsonProperty can be used as a workaround.

data class Workaround1(
    @JsonProperty("ΓΈx")
    val ox: String,
)

data class Workaround2(
    @get:JsonProperty("ΓΈx")
    val ΓΈx: String,
)

Activity

  1. cowtowncoder commented on Apr 27, 2026

    @cowtowncoder
    Member

    Likely due to FasterXML/jackson-databind#2882 which is mentioned in 3.0 Release notes (https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#behavioral-changes).
    So, intentional change on Java side at least; not sure about Kotlin module -- but I think implementation should actually consider non-ASCII letters too.

    Could you file a jackson-databind issue for this one too? Apologies for extra work.

    NOTE: plain @JsonProperty would probably work as work-around, without needing to duplicate name.

  2. olesls commented on Apr 28, 2026

    @olesls
    Author

    Thanks! Just want to make sure I understand the issue here before filing an issue in jackson-databind.
    Consider this example:

    data class Foo(
        // Kotlin Bytecode: public final getΓΈx()Ljava/lang/String;
        val ΓΈx: String = "test",
    
        // Kotlin Bytecode: public final getØk()Ljava/lang/String;
        val Øk: String = "test",
    
        // Kotlin Bytecode: public final getName()Ljava/lang/String;
        val name: String = "test",
    )
    
    fun main() {
        val jackson2Mapper = com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
        val jackson2Json = jackson2Mapper.writeValueAsString(Foo())
        println("Jackson 2: $jackson2Json")
    
        val jackson3Mapper = tools.jackson.module.kotlin.jacksonMapperBuilder().build()
        val jackson3Json = jackson3Mapper.writeValueAsString(Foo())
        println("Jackson 3: $jackson3Json")
    
        /*
        Output:
        Jackson 2: {"ΓΈx":"test","name":"test","ΓΈk":"test"}
        Jackson 3: {"Øk":"test","name":"test"}
         */
    }

    When checking the Kotlin Bytecode, I see that fields starting with a non ASCII character does not get a uppercase letter in the getter. However, if the field starts with an uppercase non ASCII character, it gets a uppercase letter in the getter, and then the field is serialized with Jackson3.

    This makes me think that the real issue is on the Kotlin side: Kotlin generates getøx() instead of getØx().
    Should this be handled in jackson-module-kotlin instead, since the Kotlin compiler is the one generating the "non-standard" getter name?

    Using accessorNaming (from your link https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#behavioral-changes) is also a possible workaround.

    .accessorNaming(
        DefaultAccessorNamingStrategy.Provider()
            .withFirstCharAcceptance(true, false),
    )
  3. cowtowncoder commented on Apr 28, 2026

    @cowtowncoder
    Member

    @olesls Good point. Perhaps issue is indeed with Kotlin-side logic; and at least needs to be traced here first.

    Looking at jackson-databind DefaultAccessorNamingStrategy.java I think it is checking for non-ASCII characters correctly, not limited to just ASCII.

    So it does indeed sound like Kotlin is not recognizing non-ASCII leading character.
    And if so, suggested work-around (allow leading lower-case letter) should work.
    But ideally somehow Kotlin handling could be improved (but generation is not under Jackson's control so...).

  4. k163377 commented on May 2, 2026

    @k163377
    Contributor

    I don't believe this is a bug, at least not in my opinion.
    Both Kotlin and Jackson are functioning as designed.
    If I had to guess, it might be considered a bug in Kotlin, but since it involves a change that affects the bytecode, it's highly unlikely to be fixed.

    It seems that this is rarely an issue, and there are manual workarounds, so I don't currently intend to actively fix it as part of the kotlin-module.


    Besides using JsonProperty or withFirstCharAcceptance, adding the JvmField annotation to the property also seems to work as a workaround.

  5. olesls commented on May 4, 2026

    @olesls
    Author

    I agree. It is more of a question about backwards compatibility. Given the workarounds, and the breaking changes mentioned here: https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#behavioral-changes, I think it makes sense to not fix this issue. But maybe it is worth mentioning it in the 'behavioral changes'-section, in case any more unlucky souls stumble upon it.

  6. k163377 commented on May 7, 2026

    @k163377
    Contributor

    Since this is a Kotlin-specific issue, would it be better to add it to Changes, JVM Languages?
    https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#kotlin

    @cowtowncoder
    I'm thinking of adding the following; what do you think?

    • #952: ...
    • #1154: Due to changes in databind#2882 and Kotlin's bytecode generation specification, fields starting with lowercase non ASCII characters are silently dropped when serializing now.
  7. cowtowncoder commented on May 7, 2026

    @cowtowncoder
    Member

    @k163377 Good idea! Maybe "dropped" -> "ignored" or "excluded"?

  8. k163377 commented on May 8, 2026

    @k163377
    Contributor

    @cowtowncoder
    Thank you.
    I've adopted "ignored" and updated the wiki.
    https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#kotlin

    With the above update, this issue will be closed.

  9. cowtowncoder commented on May 8, 2026

    @cowtowncoder
    Member

    @k163377 It would make sense to also mention this earlier in the Wiki:

    https://github.com/FasterXML/jackson/wiki/Jackson-Release-3.0#behavioral-changes

  10. k163377 commented on May 8, 2026

    @k163377
    Contributor

    @cowtowncoder
    Is this what you mean?


    Behavioral changes

    jackson-databind

    ...

    Kotlin

    • #1154: Due to changes in databind#2882 and Kotlin's bytecode generation specification, fields starting with lowercase non ASCII characters are silently ignored when serializing now.
  11. cowtowncoder commented on May 8, 2026

    @cowtowncoder
    Member

    Yes. Although "Kotlin" -> "jackson-module-kotlin"

  12. k163377 commented on May 9, 2026

    @k163377
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions