Not quite sure how best to explain the problem, so I demonstrated the same code as java and kotlin to better demonstrate.
When I read JSON, it seems like it forces the data beans value to NULL, even if this parameter is not part of json to start with, and the bean data by default sets the value of the missing field.In java, it works correctly, never trying to reduce the value that never provided for starters. In Kotlin, it seems to break because it tries to flatten the value of a non-nullable field.
In Kotlin
data class Pojo(val name: String, val age: Int, val list: List<String> = emptyList(), val ts: Date = Date()) private val mapper: ObjectMapper = ObjectMapper().registerModule(KotlinModule()) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) fun main(args: Array<String>) { mapper.readValue("""{"name": "John Doe", "list": ["yellow", "green"], "age": 42}""", Pojo::class.java) }
What causes an exception
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method Pojo.<init>, parameter ts
In Java (everything works fine)
public class Transform { private static ObjectMapper mapper = new ObjectMapper() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); public static class Pojo { private String name; private int age; private List<String> list; private Date ts = new Date(); <getters and setters for all> } public static void main(String[] args) throws IOException { String json = "{\"name\": \"John Doe\", \"list\": [\"yellow\", \"green\"], \"age\": 42}"; Pojo p = mapper.readValue(json, Pojo.class); System.out.printf("Bean: name=%s, age=%s, list=%s, ts=%s\n", p.name, p.age, p.list, p.ts); } }
Even if I make it a class instead of a data class in kotlin, it still mistakenly does the same thing.
My question is: how can I get Jackson's deserialization to work in Kotlin with my POJOs. The expected behavior is that it "should" break if something is passed for a null value or an incorrect value where null is not allowed. But in the above scenario, where no attempt was made to change the ts field to zero, it should have used the default value, as for java.
The only thing that seems to work is not to use the bean data concept at all and write my beans as
class Pojo(val name: String, val age: Int) { var list: List<String> = emptyList() var ts: Date = Date() }
But then my .equals does not work, and it allows other downstream to manipulate the contents of the list and ts properties when I want them to be read-only.