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

First two characters of property name must be lowercase due to Java restrictions #92

Closed
robpridham-bbc opened this issue Oct 12, 2017 · 10 comments

Comments

@robpridham-bbc
Copy link

robpridham-bbc commented Oct 12, 2017

I discovered today by chance that an old restriction from Java means that you cannot have uppercase characters in the first two characters of a JSON/Kotlin property.

Consider this self-contained test case:


import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.Assert
import org.junit.Test

class DataClassTest2 {

    data class DataClass1(val zName: String)

    data class DataClass2(val zname: String)

    private val jsonData1 = """
        {
            "zName": "my name"
        }
        """

    private val jsonData2 = """
        {
            "zname": "my name"
        }
        """

    @Test
    fun testJsonParsing1() {
        val mapper = jacksonObjectMapper()
        val dataClass1 = mapper.readValue<DataClass1>(jsonData1)
        Assert.assertEquals("my name", dataClass1.zName)
    }

    @Test
    fun testJsonParsing2() {
        val mapper = jacksonObjectMapper()
        val dataClass2 = mapper.readValue<DataClass2>(jsonData2)
        Assert.assertEquals("my name", dataClass2.zname)
    }
}

The first test fails and the second passes.

This is because of this and by reference this.

However in the world of Kotlin, we are several steps removed from Java beans or indeed the underlying setters/getters that Kotlin automatically generates, and this behaviour doesn't make a great deal of sense. It was only by accident that I discovered it.

Please consider whether there's an opportunity to bypass this issue via the Kotlin module.

@cowtowncoder
Copy link
Member

Have you tried enabling MapperFeature.USE_STD_BEAN_NAMING?

@robpridham-bbc
Copy link
Author

robpridham-bbc commented Oct 12, 2017

I have now, and it doesn't seem to change anything.

@Test
    fun testJsonParsing1() {
        val mapper = jacksonObjectMapper()
        mapper.configure(MapperFeature.USE_STD_BEAN_NAMING, true);
        val dataClass1 = mapper.readValue<DataClass1>(jsonData1)
        Assert.assertEquals("my name", dataClass1.zName)
    }

It also looks like, even if it had worked, this is going away in 3.0.

@cowtowncoder
Copy link
Member

Yes, feature goes away because it becomes default, and there is no real reason to support previous (2.x) behavior since it was not compatible with Bean naming (for specific case of more than one leading capital letters).

But it sounds like this is not related to name mangling core databind does, given that behavior does not change. Apologies for noise if this is the case. Just thought it is good to eliminate one possibility.

@robpridham-bbc
Copy link
Author

robpridham-bbc commented Nov 3, 2017

I debugged this today and it occurs because of an inconsistency in how Kotlin names things, which is not handled in how POJOPropertiesCollector expects to find them.

The class definition:

data class DataClass1(val zName: String)

produces something resembling this Java:

public final class DataClass1 {
   @NotNull
   private final String zName;

   @NotNull
   public final String getZName() {
      return this.zName;
   }
...
}

As you can see, there is an inconsistency between the field name and the getter (or the constructor argument). Changing to use standard bean naming doesn't help, because then instead of zName vs ZName, you have zName vs zname. Therefore the collection process ends having collected one getter and one constructor parameter with different keys.

@cowtowncoder
Copy link
Member

@robpridham-bbc Ah. Yes, I see the issue. Thank you for debugging this further.

From Jackson perspective names are inconsistent.... Bean naming would get ZName out of getZName, which does not match with zName. I suspect custom NamingStrategy might work here but it's unfortunate if that needs to be used.
But I am also not sure how this could be achieved in any other supported way.

@apatrida
Copy link
Member

This is related to other issues reported here, Kotlin generates setter/getters and those do not match the naming in the constructor parameter. It is hard to align everything in a way that Jackson would accept. The only guaranteed way is to use JsonProperty annotations, but even then they will work differently if set on the constructor vs. the property. I am looking at options for these issues.

@Fleshgrinder
Copy link

I ran into the exact same problem today. Any workarounds that one can use for this?

@apatrida
Copy link
Member

Only using JsonProperty annotations, there is no way to get Kotlin naming for properties and Jackson databind to line up perfectly in all cases.

@akosmenyhert
Copy link

Use the @JsonProperty annotation to the accessor methods.

For example: @get:JsonProperty("ID") val id: String = ""

@MaiconCanedo
Copy link

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

6 participants