Background
Hey there, while developing with Spring Boot, I encountered issues with Jackson deserialization when storing historical messages into the Redis client. After troubleshooting using the control variable method, I found that there is room for improvement in the classes under the Message package. I made two code modifications to accommodate the new version of Jackson's deserialization process.
Solution
- I added no-argument constructors to
UserMessage
,SystemMessage
,FunctionMessage
, andAssistMessage
, and modifiedAbstractMessage
to include a single-argument constructor. This resolves the following serialization error:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `org.springframework.ai.chat.messages.SystemMessage` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
- I renamed
textContent
andmediaData
inAbstractMessage
tocontent
andmedia
to align with the methods exposed by the message. This change can resolve the following error:
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Problem deserializing 'setterless' property ("media"): no way to handle typed deser with setterless yet
PS: Additional Information
Version Information: Spring Boot 3.2.4, spring-boot-starter-data-redis 3.2.4, SpringAI 1.0.0-SNAPSHOT
RedisTemplateConfig.java:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
return new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
}
}
Comment From: markpollack
Can we use the approach that adds Jackson annotations since creating an instance of a message without content in code does not make sense. For example.
class MyClass {
private final int id;
private final String name;
@JsonCreator
public MyClass(@JsonProperty("id") int id, @JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
....
Comment From: AZCodingAccount
thx