vertx-lang-kotlin icon indicating copy to clipboard operation
vertx-lang-kotlin copied to clipboard

JsonObject,mapTo no support data class

Open ZenLiuCN opened this issue 7 years ago • 15 comments

I got this data class

data class User(
  val Id: ObjectId,
  var Phone: String,
  var Name: String = "",
  var Gender: String = ""
)

when use JsonObject.mapTo(User::class.java), got Error java.lang.IllegalArgumentException: Cannot construct instance of User (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3738) at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3656) at io.vertx.core.json.JsonObject.mapTo(JsonObject.java:105)

it's sames error from jackson , and I found https://github.com/FasterXML/jackson-module-kotlin may fix this,will it be updated ?

ZenLiuCN avatar Dec 01 '17 02:12 ZenLiuCN

I think that you should use this library along with Vert.x. you can access Jackson factories as static members of the io.vertx.core.json.Json class and add this module yourself when you start the application.

vietj avatar Jan 08 '18 13:01 vietj

already done that ,Thanks for Attention~

ZenLiuCN avatar Jan 09 '18 01:01 ZenLiuCN

object JacksonFix {
  var isFixed:Boolean=false
  fun fixVertxJson() {
    if(!isFixed){
       Json.mapper.registerModule(com.fasterxml.jackson.databind.module.SimpleModule()
      .addSerializer(JsonObject::class.java, object : com.fasterxml.jackson.databind.JsonSerializer<JsonObject>() {
        override fun serialize(value: JsonObject, gen: JsonGenerator, serializers: SerializerProvider?) {
          gen.writeRawValue(if (value.isEmpty) "{}" else value.encode())
        }
      })
      .addSerializer(JsonArray::class.java, object : com.fasterxml.jackson.databind.JsonSerializer<JsonArray>() {
        override fun serialize(value: JsonArray, gen: JsonGenerator, serializers: SerializerProvider) {
          gen.writeRawValue(if (value.isEmpty) "[]" else value.encode())
        }
      }))
    Json.mapper.registerKotlinModule().findAndRegisterModules()
 isFixed=true
   }
  }
}

simple to use it

 JacksonFix. fixVertxJson()

or in java

 JacksonFix.INSTANCE.fixVertxJson();

those code is what i'm using for

  1. fix vertx.core.json serial empty JsonObject as {"map":{}}
  2. fix vertx.core.json serial empty JsonArray as {"list":[] } note to avoid json format miss match for other systems
  3. registerd https://github.com/FasterXML/jackson-module-kotlin

Notes

May not have vertx.core.json element as property type,it will not be Deserialized properly use Map<String,Any> to replace JsonObject AND MutableSet<Any> to Replace JsonArray()


hope could be help for those one who have same needs

ZenLiuCN avatar Jan 09 '18 02:01 ZenLiuCN

how about blogging about it ?

vietj avatar Jan 09 '18 07:01 vietj

I usually do this to add support for jackson-module-kotlin

 Json.mapper.apply {
        registerKotlinModule()
    }

    Json.prettyMapper.apply {
        registerKotlinModule()
    }

as long kotlin module is registered to Json mapper, it will be fixed.

@vietj how about to make kotlin module enabled by default?

jasoet avatar Mar 02 '18 04:03 jasoet

@jasoet's solution worked for me: IMO this is something that should be added as a dependency since it would be nice to marshal arbitrary JSON into data classes without having to think about it in Kotlin.

mach-kernel avatar May 03 '18 17:05 mach-kernel

@jasoet we don't enable module plugin, people should register it. @mach-kernel to what should it be added ? vertx-lang-kotlin artifact ?

vietj avatar May 03 '18 20:05 vietj

Can't get this to work....

// Here I have Instant which is giving me problems even though I'm using
//     compile group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.9.7'
data class Workflow(
    var key: String,
    var script: String,
    var created: Instant = Instant.now(),
    var udpated: Instant = Instant.now(),
){}

// I register the mappers on  io.vertx.core.json.Json
Json.mapper.apply {
            registerKotlinModule()
            findAndRegisterModules()
}


val workflow = Workflow("testScript", "println(\"Hello World\")")
val strJSON = JsonObject.mapFrom(workflow)
eventBus.send("addres", strJson) // won't work 
eventBus.send("addres", strJson.encode()) // does work but then I have an extra step


If I send on the event bus a message as a string using strJSON.encode() then it works, because at the consumer I can take the string convert it to JsonObject(strJSON) then use mapTo and it works. Strange because I would think that eventbus would use the same method to convert. If I send the straight JsonObject I get an error

java.lang.IllegalStateException: Illegal type in JsonObject: class java.math.BigDecimal

Can anyone point me to what I'm missing? @vietj ?

Update:

Looks like when I send a JsonObject through eventbus it uses JsonObjectMessageCodec which serializes the object to buffer and it doesn't use Jackson. Maybe we can add support for sending objects as strings, then it can use Jackson to serialize for all objects?

lfmunoz avatar Nov 09 '18 00:11 lfmunoz

@vietj Why not add and enable jackson-module-kotlin by default in vertx-lang-kotlin? I would assume everyone that uses Kotlin and Jackson in the same project would need to (de)serialize JSON from/to data classes at some point, they are just the natural representation for exchange formats like this and it would be nice if it worked out of the box.

dklotz avatar May 29 '19 22:05 dklotz

how could it be enabled by default ?

vietj avatar Apr 23 '20 17:04 vietj

One way would be with an extension function since these already exist for kotlin anyways (e.g. #jsonArrayOf etc.).

  private var kotlinModuleRegistered = false

  fun <T> JsonObject.toDataClass(type: Class<T>): T {
    if(!kotlinModuleRegistered) {
      kotlinModuleRegistered = true
      DatabindCodec.mapper().registerKotlinModule()
    }
    return mapTo(type)
  }

I don't know if this needs to be synchronized but the name can surely be improved :smile:

wowselim avatar Apr 24 '20 05:04 wowselim

yes but if the user don't use them, then they are not loaded and no registration is done I think

vietj avatar Apr 24 '20 06:04 vietj

It looks like there is a way for jackson to discover and register modules on the classpath. I believe vertx already does some configuration inside io.vertx.core.json.Json#initialize. This would be the perfect place to call this code. Then the only thing left to do is to add the kotlin module as a dependency for vertx-lang-kotlin. If that sounds good then I can create PRs for this.

wowselim avatar Apr 26 '20 05:04 wowselim

I am not in favour of this option because it is an open door for potential security issues

On 26 Apr 2020, at 07:35, Selim Dincer [email protected] wrote:

It looks like there is a way for jackson to discover and register modules on the classpath https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/ObjectMapper.html#findAndRegisterModules(). I believe vertx already does some configuration inside io.vertx.core.json.Json#initialize. This would be the perfect place to call this code. Then the only thing left to do is to add the kotlin module as a dependency for vertx-lang-kotlin. If that sounds good then I can create PRs for this.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/vert-x3/vertx-lang-kotlin/issues/43#issuecomment-619485627, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABXDCTQ27IIJUZ2PYNWMILROPB3PANCNFSM4EGHBVCA.

vietj avatar Apr 26 '20 09:04 vietj

Is there an opportunity to use kotlinx.serialization here instead?

My understanding is that encoders/decoders are generated at compile-time, which would mitigate security concerns associated with Jackson's use of reflection. Not to mention the benefit of a Kotlin-native serialization library.

rgmz avatar May 31 '21 15:05 rgmz

See solution in https://github.com/vert-x3/vertx-lang-kotlin/issues/43#issuecomment-356157748

tsegismont avatar Oct 06 '23 15:10 tsegismont