spring-ai icon indicating copy to clipboard operation
spring-ai copied to clipboard

Java 8 date/time type `java.time.Duration` not supported by default

Open newzhiMing opened this issue 7 months ago • 7 comments
trafficstars

Caused by: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class java.time.Duration] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:489) at org.springframework.http.converter.AbstractGenericHttpMessageConverter.writeInternal(AbstractGenericHttpMessageConverter.java:123) at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:235) at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.sendInternal(ResponseBodyEmitterReturnValueHandler.java:221) at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.send(ResponseBodyEmitterReturnValueHandler.java:212) at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.sendInternal(ResponseBodyEmitter.java:223) ... 8 common frames omitted Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.Duration not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: org.springframework.ai.chat.model.ChatResponse["metadata"]->org.springframework.ai.chat.metadata.ChatResponseMetadata["rateLimit"]->org.springframework.ai.chat.metadata.EmptyRateLimit["tokensReset"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1330) at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:183) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:183) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:183) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:502) at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:341) at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1574) at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1061) at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:483) ... 13 common frames omitted

newzhiMing avatar Apr 21 '25 02:04 newzhiMing

Could you please share your related code?

dev-jonghoonpark avatar Apr 21 '25 02:04 dev-jonghoonpark

yes,thanks! `@PostMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Operation(summary = "chat") public Flux<ChatResponse> chat() throws InterruptedException { ChatClient.ChatClientRequestSpec chat = chatClient.prompt( new Prompt("hello!!!", options)); Flux<ChatResponse> chatResponseFlux = chat.stream().chatResponse(); return chatResponseFlux; }

` environment:spring ai 1.0.0-M7 springboot 3.3.4

newzhiMing avatar Apr 21 '25 03:04 newzhiMing

  • Which model are you using?
  • How did you initialize the options object?

It worked fine with the Anthropic model.

dev-jonghoonpark avatar Apr 21 '25 04:04 dev-jonghoonpark

The name of the model I use is gpt-4o-2024-11-20, which is one of the many models of GPT。 OpenAiChatOptions options = OpenAiChatOptions.builder().model("gpt-4o-2024-11-20").build(); I don't think the problem is that it comes out of the model, but that I return to the Flux<ChatResponse>.I think the reason may be that there is a type Duration in the property of object ChatResponse, which is an error reported by the json handler during serialization, but it cannot be solved by configuring the ObjectMapper.The ObjectMapper code I configured is as follows (it doesn't work): @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); return mapper; } Thank you, seniors!

newzhiMing avatar Apr 21 '25 07:04 newzhiMing

Thanks seniors, I solved it!

newzhiMing avatar Apr 21 '25 07:04 newzhiMing

Thanks seniors, I solved it!

@newzhiMing Could you post your solution and close this issue? 😄

reneleonhardt avatar Apr 23 '25 15:04 reneleonhardt

The name of the model I use is gpt-4o-2024-11-20, which is one of the many models of GPT。 OpenAiChatOptions options = OpenAiChatOptions.builder().model("gpt-4o-2024-11-20").build(); I don't think the problem is that it comes out of the model, but that I return to the Flux.I think the reason may be that there is a type Duration in the property of object ChatResponse, which is an error reported by the json handler during serialization, but it cannot be solved by configuring the ObjectMapper.The ObjectMapper code I configured is as follows (it doesn't work): @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); return mapper; } Thank you, seniors!

ould you post your solution and close this issue?

luwanglin avatar Apr 27 '25 15:04 luwanglin

OK,This issue arises because of an issue with the JSON serializer that SpringBoot returns to the frontend. Here's how I worked: public static ObjectMapper getJsonObjectMapper() { var objectMapper = new ObjectMapper(); var simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Duration.class, DurationSerializer.INSTANCE); // This is the key code simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); objectMapper.registerModule(simpleModule); return objectMapper; }

newzhiMing avatar May 13 '25 05:05 newzhiMing