How to Serialize JMS Messages as JSON with Spring's MessageConverter
Problem
When I tried to send a POJO over JMS in my Spring Boot application, I got this error:
org.springframework.jms.support.converter.MessageConversionException: Failed to resolve type for message payload at org.springframework.jms.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:78)I expected Spring to automatically convert my object to JSON. Instead, I found that Spring JMS uses SimpleMessageConverter by default, which only handles three types: String, byte[], and Map.
Environment
- Spring Boot 3.2.x
- Java 21
- ActiveMQ / IBM MQ (any JMS broker)
- Jackson 2.15.x
What happened?
I was building a messaging system where I needed to send article objects between services. Here’s my POJO:
public record Article( Long id, String title, String content, String author, LocalDateTime publishedAt) {}I tried sending it directly with JmsTemplate:
@Servicepublic class ArticlePublisher {
@Autowired private JmsTemplate jmsTemplate;
public void publishArticle(Article article) { jmsTemplate.convertAndSend("article.queue", article); }}And my listener:
@Componentpublic class ArticleListener {
@JmsListener(destination = "article.queue") public void receiveArticle(Article article) { System.out.println("Received article: " + article.title()); }}When I ran the application, the SimpleMessageConverter threw an error because it cannot serialize POJOs directly. It only knows how to handle basic types.
How to solve it?
I needed to implement a custom MessageConverter that uses Jackson’s JsonMapper to serialize objects as JSON. Here’s my solution:
package com.example.config;
import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.JsonMapper;import jakarta.jms.JMSException;import jakarta.jms.Message;import jakarta.jms.Session;import jakarta.jms.TextMessage;import org.springframework.jms.support.converter.MessageConversionException;import org.springframework.jms.support.converter.MessageConverter;
public class JsonMessageConverter implements MessageConverter {
private final JsonMapper jsonMapper;
public JsonMessageConverter() { this.jsonMapper = JsonMapper.builder() .findAndAddModules() .build(); }
@Override public Message toMessage(Object object, Session session) throws JMSException, MessageConversionException { String json; try { json = jsonMapper.writeValueAsString(object); } catch (JsonProcessingException e) { throw new MessageConversionException("Failed to serialize object to JSON", e); }
TextMessage message = session.createTextMessage(json); message.setStringProperty("_type", object.getClass().getName());
return message; }
@Override public Object fromMessage(Message message) throws JMSException, MessageConversionException { if (!(message instanceof TextMessage textMessage)) { throw new MessageConversionException("Expected TextMessage but received: " + message.getClass().getName()); }
String json = textMessage.getText(); String typeName = message.getStringProperty("_type");
if (typeName == null) { throw new MessageConversionException("Missing _type property in message"); }
try { Class<?> type = Class.forName(typeName); return jsonMapper.readValue(json, type); } catch (ClassNotFoundException e) { throw new MessageConversionException("Unknown type: " + typeName, e); } catch (JsonProcessingException e) { throw new MessageConversionException("Failed to parse JSON: " + json, e); } }}I can explain the key parts:
JsonMapperis more efficient thanObjectMapperfor simple use casesfindAndAddModules()enables support for Java 8+ types likeLocalDateTime_typeproperty stores the fully qualified class name for reconstructiontoMessage()serializes the POJO to JSON and wraps it in aTextMessagefromMessage()deserializes JSON back to the original type using the_typeproperty
Configuration
I registered the converter as a Spring bean:
package com.example.config;
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jms.support.converter.MessageConverter;
@Configurationpublic class JmsConfig {
@Bean public MessageConverter jsonMessageConverter() { return new JsonMessageConverter(); }}Spring Boot auto-wires this MessageConverter bean into:
JmsTemplatefor sending messages@JmsListenermethods for receiving messagesJmsClientfor synchronous operations
Sending and Receiving
Now my publisher works without any changes:
@Servicepublic class ArticlePublisher {
@Autowired private JmsTemplate jmsTemplate;
public void publishArticle(Article article) { // JsonMessageConverter automatically converts Article to JSON jmsTemplate.convertAndSend("article.queue", article); }}And my listener automatically receives typed objects:
@Componentpublic class ArticleListener {
@JmsListener(destination = "article.queue") public void receiveArticle(Article article) { // JsonMessageConverter deserializes JSON back to Article System.out.println("Received article: " + article.title()); }}The Reason
The SimpleMessageConverter has limited support for object types:
| Type | Support |
|---|---|
String | Yes - sent as TextMessage |
byte[] | Yes - sent as BytesMessage |
Map | Yes - sent as MapMessage |
| POJO | No - throws MessageConversionException |
The _type property is critical for deserialization. Without it, Jackson wouldn’t know what class to deserialize the JSON into. The message would just be a generic JSON string with no type information.
Here’s what the JSON message looks like on the wire:
{ "id": 123, "title": "Understanding Spring JMS", "content": "A comprehensive guide...", "author": "cowrie", "publishedAt": "2026-03-26T10:00:00"}And the JMS message properties include:
_type = com.example.ArticleCommon Mistakes
I made several mistakes while implementing this:
1. Forgetting the _type Property
Without the _type property, I couldn’t reconstruct the original object:
@Overridepublic Message toMessage(Object object, Session session) throws JMSException { String json = jsonMapper.writeValueAsString(object); return session.createTextMessage(json); // Missing _type!}The deserializer wouldn’t know what class to use.
2. Not Handling Non-TextMessage Scenarios
JMS supports multiple message types. I initially assumed all messages would be TextMessage:
@Overridepublic Object fromMessage(Message message) throws JMSException { TextMessage textMessage = (TextMessage) message; // ClassCastException! // ...}This throws ClassCastException when receiving BytesMessage or ObjectMessage.
3. Using ObjectMapper Instead of JsonMapper
Jackson 2.13+ provides JsonMapper which is more efficient:
private final ObjectMapper objectMapper = new ObjectMapper(); // Works but slowerprivate final JsonMapper jsonMapper = JsonMapper.builder() .findAndAddModules() .build();4. Throwing Generic Exceptions
I initially threw RuntimeException which made debugging harder:
throw new RuntimeException("Failed to convert message");Using MessageConversionException provides better error context:
throw new MessageConversionException("Failed to convert message", cause);Summary
In this post, I showed how to implement a custom JMS MessageConverter to serialize POJOs as JSON in Spring Boot. The key points are:
SimpleMessageConvertercannot serialize POJOs directly- Custom
MessageConverterdelegates to Jackson’sJsonMapperfor JSON conversion - The
_typemessage property stores the fully qualified class name for reconstruction - Spring Boot auto-wires
MessageConverterbeans intoJmsTemplateand@JmsListener
The implementation handles serialization, deserialization, and type preservation. This pattern works with any JMS broker: ActiveMQ, IBM MQ, RabbitMQ, or Amazon SQS.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments