Search code examples
kotlinwebsocketbinarypostmanspring-websocket

How do I call a STOMP message with Postman using injection?


I have the following...

@MessageMapping("/hello")
@SendTo("/topic/greetings")
@Throws(Exception::class)
fun greeting(message: TestMessage): Greeting {

However, when I run and send a message I get

Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.gleason.love_monkey.model.TestMessage` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1754) ~[jackson-databind-2.18.2.jar:2.18.2]

I tried changing to

    fun greeting(message: Any): Greeting {
        val jsonString: String
        // Log the incoming message
        if (message is ByteArray) {
            jsonString = byteArrayToString(message)
        } else {
            jsonString = objectMapper.writeValueAsString(message)
        }
        logger.info("Received message: $jsonString")

And when I do that I see that it is byteEncoded but correct...

2025-01-31T18:40:40.006-05:00  INFO 49504 --- [love-monkey] [nboundChannel-1] o.g.l.controller.WebsocketController     : Received message: {"name":"me"}

The frame was made using the process described here

enter image description here

The data class is

data class TestMessage(val name: String)

What am I missing how can I type the message and still use postman

Update

I tried changing to

        val jsonString: String
        // Log the incoming message
        if (message is ByteArray) {
            jsonString = byteArrayToString(message)
        } else {
            jsonString = objectMapper.writeValueAsString(message)
        }
        logger.info("Received message: $jsonString")

        // Log the JSON string before deserialization
        logger.info("JSON string to be deserialized: ${jsonString.trim()}")

        val testMessage: TestMessage = objectMapper.readValue(jsonString.trim(), TestMessage::class.java)

And when I debug into the object mapper it looks correct...

enter image description here

But it still throws

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.gleason.love_monkey.model.TestMessage` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1754) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1379) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1512) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4917) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3860) ~[jackson-databind-2.18.2.jar:2.18.2]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3828) ~[jackson-databind-2.18.2.jar:2.18.2]
    at org.gleason.love_monkey.controller.WebsocketController.greeting(WebsocketController.kt:35) ~[main/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMatch(AbstractMethodMessageHandler.java:568) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:530) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler.handleMatch(SimpAnnotationMethodMessageHandler.java:93) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessageInternal(AbstractMethodMessageHandler.java:522) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(AbstractMethodMessageHandler.java:457) ~[spring-messaging-6.2.2.jar:6.2.2]
    at org.springframework.messaging.support.ExecutorSubscribableChannel$SendTask.run(ExecutorSubscribableChannel.java:152) ~[spring-messaging-6.2.2.jar:6.2.2]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

Per the answer I tried updating to

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
...
private val objectMapper = ObjectMapper().registerKotlinModule()
...
val testMessage: TestMessage = objectMapper.readValue(jsonString.trim(), TestMessage::class.java)

But I still get...

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of org.gleason.love_monkey.model.TestMessage (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 2]

Raw message

SEND
destination:/app/hello
content-length:19
content-type:application/json

{"name":"Me"}


Solution

  • If you use injected objectMapper, check that its definition contains registration of the Kotlin module.

    So it should be like this

    @Bean
    fun objectMapper() = ObjectMapper().registerKotlinModule()
    

    In order to have .registerKotlinModule(), you need to have this dependency

    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
    

    It should function the same regardless of whether the input is a String or a ByteArray.