0
votes

I need to de-serialize optional values from json to the data class in Kotlin, which have Option types. Example:

data class Sample(val id: Long, val content: Option<String>)

fun main() {
  val mapper = ObjectMapper()
  val v1 = mapper.readValue("""
  { "id": "1", "content": null }
  """.trimIndent(), Sample::class.java)


  val v2 = mapper.readValue("""
  { "id": "2", "content": "Some content" }
  """.trimIndent(), Sample::class.java)

  assert(v1.id == 1)
  assert(v1.content == Option.empty())
  assert(v2.id == 2)
  assert(v2.content == Option.just("Some content"))
}

I'm struggling to write correct version of deserializer for this use case. Here is what I've tried:

class OptionDeserializer<T>(private val clazz: Class<T>) : StdDeserializer<Option<T>>(clazz) {

  override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Option<T> {
      val value = p?.codec?.readValue(p, clazz)
      return if (value == null) Option.empty() else Option.just(value)
  }
}

fun main() {
  val mapper = ObjectMapper()
  val module = SimpleModule("Option")
  module.addDeserializer(Option::class.java, 
    OptionDeserializer(Option::class.java))
  mapper.registerModule(module)

  val v1 = mapper.readValue("""
  { "id": "1", "content": "Some content" }
  """.trimIndent(), Sample::class.java)

  assert(v1.id == 1)
  assert(v1.content == Option.just("Some content"))
}

But this doesn't work and throws error:

Cannot construct instance of $Sample (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{ "id" : "1", "content" : "Some content" }"; line: 2, column: 3] com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of $Sample (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (String)"{ "id" : "1", "content" : "Some content" }"; line: 2, column: 3] at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)

How to get jackson to work with the Option type from arrow-kt?

2
The error is about default-constructor. Declare default constructor or use kotlin-no-arg plugin.galcyurio
Use Option.fromNullable() instead of manual null checking.galcyurio

2 Answers

0
votes

You need to register the Kotlin module

val mapper = ObjectMapper().registerModule(KotlinModule())

Here's the Maven dependency, in case you don't have it yet.

0
votes

Another option is using a custom serializer. There is no need to register anything explicitly in that case. See suggested usage here.