Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonProperty is ignored on data class properties with names starting with "is" #237

Closed
MartinHaeusler opened this issue Jul 16, 2019 · 17 comments

Comments

@MartinHaeusler
Copy link

Hi,

I've got a data class with a boolean property called isPublic. This property needs to be called exactly like that in JSON. However, since the property name starts with is, the prefix is stripped from the name. Adding @JsonProperty("isPublic") does not work either:

class JacksonTest {

    @Test
    fun serializeTest(){
        val objectMapper = ObjectMapper()
        objectMapper.registerKotlinModule()

        val instance = MyClass(true)
        val json = objectMapper.writeValueAsString(instance)

        assertEquals("""{"isPublic":true}""", json.replace("""\s+""".toRegex(), ""))
    }

    data class MyClass(
        @JsonProperty("isPublic")
        val isPublic: Boolean
    )

}

... fails with an assertion error:

java.lang.AssertionError: Expected <{"isPublic":true}>, actual <{"public":true}>.

It doesn't matter what you pass into the @JsonProperty annotation, in the JSON output the field is always (!!) called public.

@MartinHaeusler
Copy link
Author

I just figured out that this only happens if the field name starts with is. On all other field names, the @JsonProperty annotation is respected as intended.

@MartinHaeusler MartinHaeusler changed the title @JsonProperty is ignored on data class properties @JsonProperty is ignored on data class properties with names starting with "is" Jul 16, 2019
@ghost
Copy link

ghost commented Jul 25, 2019

This problem is extra extra confusing when using an event based framework like Axon with Kotlin. A boolean with is in the name will always come out false inside of the event since Axon uses Jackson internally to serialize and deserialize events.

This turned out to be a huge waste of time until one of us remembered this was a known issue. Is there any plan to fix this problem?

@cowtowncoder
Copy link
Member

I am not aware of anyone working on the issue. New contributors to Kotlin module would be highly appreciated, as its usage has been increasing rapidly along with adoption of Kotlin.
But I don't personally have enough foundational knowledge of Kotlin as platform to be of much help unfortunately.
I could try a Bat Signal on mailing lists, but I have rarely had much success when asking for something other than very specific help (f.ex I did get contributors to help with Scala module release).
Ownership is something that you can't really sell or advertise for.

@rafalschmidt97
Copy link

Same! Any solution? I thought that I made mistake somewhere in the configuration.

My code:

data class PlayerResponse(
  @JsonProperty("abc")
  val accountId: Long,

  @JsonProperty("def")
  val isAdmin: Boolean,

  @JsonProperty("ghi")
  val isConnected: Boolean
)

val mapper = jacksonObjectMapper().apply {
    registerModule(ParameterNamesModule())
    registerModule(Jdk8Module())
    registerModule(JavaTimeModule())
  }

  val json = mapper.writeValueAsString(PlayerResponse(1, true, true))

Response:

{"abc":1,"admin":true,"connected":true}

@MartinHaeusler
Copy link
Author

The only workaround I've found is to rename the field in your (data) class, and then expose it with @JsonProperty like so:

data class MyDTO(

    @JsonProperty("isConnected")
    val connected: Boolean   // do NOT call this "isConnected"!

)

That worked for me, YMMV.

@MaksimRT
Copy link

Same! Any solution? I thought that I made mistake somewhere in the configuration.

My code:

data class PlayerResponse(
  @JsonProperty("abc")
  val accountId: Long,

  @JsonProperty("def")
  val isAdmin: Boolean,

  @JsonProperty("ghi")
  val isConnected: Boolean
)

val mapper = jacksonObjectMapper().apply {
    registerModule(ParameterNamesModule())
    registerModule(Jdk8Module())
    registerModule(JavaTimeModule())
  }

  val json = mapper.writeValueAsString(PlayerResponse(1, true, true))

Response:

{"abc":1,"admin":true,"connected":true}

Use @get:JsonProperty("abc") and @param:JsonProperty("abc") annotation on Boolean field instead of @JsonProperty("abc")

@rafalschmidt97
Copy link

rafalschmidt97 commented Oct 18, 2019

@MaksimRT - wow it works. Thanks a lot!

@apatrida
Copy link
Member

@MartinHaeusler does the workaround as mentioned by MaksimRT a few posts up solve your issue as well?

@MartinHaeusler
Copy link
Author

I didn't try it, but it does seem plausible that it could fix the issue. The real question is: where does it come from, and can it be fixed rather than worked around? This is a very nasty bug after all, and extremely hard to search for on google, because likely you wouldn't even know what hit you.

@MaksimRT
Copy link

It will be fixed in 2.10.1 version by #256

@apatrida
Copy link
Member

@MartinHaeusler We are trying to find a way to deal with where annotations are placed and how they are interpreted, the issue is that Kotlin compiler chooses where they go, and it picks a spot that is far from good in terms of Jackson seeing them and being able to interpret them correctly.

I am looking to see if there are options to "imply" them where the are missing, or see if we can get Kotlin to consider placing them not in the "first" place it finds that matches their list, but ALL places we want them.

So it is more of incompatibility with Kotlin annotation targetting than a bug in Jackson Kotlin module. When the annotations are where we can see them safely, things work. If they end up going somewhere silly, not so much.

It is a current issue we want to solve for sure.

@erika33
Copy link

erika33 commented Dec 22, 2019

@MartinHaeusler

@MartinHaeusler
Copy link
Author

@erika33 yes?

@ishanbakshi
Copy link

Thanks @MaksimRT
Just adding @get:JsonProperty("abc") worked for me.
I did not add @param:JsonProperty("abc") and it still worked. Is there any specific reason why you added @param:JsonProperty("abc") as well?

@MaksimRT
Copy link

MaksimRT commented Aug 6, 2020

Thanks @MaksimRT
Just adding @get:JsonProperty("abc") worked for me.
I did not add @param:JsonProperty("abc") and it still worked. Is there any specific reason why you added @param:JsonProperty("abc") as well?

Hi, you are welcome)

What version of the library do you use?)

@mkheck
Copy link

mkheck commented Feb 8, 2021

Changing @JsonProperty("fieldname") to @field:JsonProperty("fieldname") resolves the issue for data classes' "is" (Boolean) properties.

@k163377
Copy link
Contributor

k163377 commented Mar 3, 2023

I have checked the following code and it seems to have already been resolved, so this issue is closed.

import com.fasterxml.jackson.annotation.JsonProperty

data class PlayerResponse(
    @JsonProperty("abc")
    val accountId: Long,

    @JsonProperty("def")
    val isAdmin: Boolean,

    @JsonProperty("ghi")
    val isConnected: Boolean
)

fun main() {
    val mapper = jacksonObjectMapper()
    val json = mapper.writeValueAsString(PlayerResponse(1, true, true))
    println(json)
}

@k163377 k163377 closed this as completed Mar 3, 2023
k163377 added a commit to k163377/jackson-module-kotlin that referenced this issue Mar 3, 2023
cowtowncoder pushed a commit that referenced this issue Mar 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants