You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中OffsetDateTime响应被序列化为BigDecimal问题排查

问题:OffsetDateTime序列化时被转为BigDecimal而非字符串

我有一个包含OffsetDateTime类型time属性的SeriesListInner实例,将其作为Response Entity返回时,预期序列化为ISO格式字符串,但实际被序列化为BigDecimal类型的时间戳。实体类和控制器映射都是通过OpenAPI生成器自动生成的,无法添加Jackson注解自定义序列化逻辑,且此前功能正常,未修改过相关配置Bean或应用属性。

生成的实体类

/**
 * 
 * @param id 
 * @param name 
 * @param episodes 
 * @param languages 
 * @param time 
 * @param imagePath Relative path to where the image is located
 */
data class SeriesListInner(

    @get:JsonProperty("id", required = true) val id: kotlin.Long,

    @get:JsonProperty("name", required = true) val name: kotlin.String,

    @get:JsonProperty("episodes", required = true) val episodes: kotlin.Int,

    @get:JsonProperty("languages", required = true) val languages: kotlin.collections.List<kotlin.String>,

    @get:JsonProperty("time", required = true) val time: java.time.OffsetDateTime,

    @get:JsonProperty("imagePath") val imagePath: kotlin.String? = null
) {

}

预期响应

[ {
  "id" : 1,
  "name" : "Series #1",
  "episodes" : 2,
  "languages" : [ ],
  "time" : "2023-07-26T01:16:11.789747Z",
  "imagePath" : null
}, {
  "id" : 2,
  "name" : "Series #2",
  "episodes" : 2,
  "languages" : [ ],
  "time" : "2023-07-26T01:16:11.789747Z",
  "imagePath" : null
} ]

实际响应

[ {
  "id" : 1,
  "name" : "Series #1",
  "episodes" : 2,
  "languages" : [ ],
  "time" : 1.690334171789747E9,
  "imagePath" : null
}, {
  "id" : 2,
  "name" : "Series #2",
  "episodes" : 2,
  "languages" : [ ],
  "time" : 1.690334171789747E9,
  "imagePath" : null
} ]

现有配置

Web配置类

@Configuration
@EnableWebMvc
class WebConfiguration : WebMvcConfigurer {
    override fun addCorsMappings(registry: CorsRegistry) {
        registry.addMapping("/**")
    }
}

应用配置文件(application.yml)

spring:
  datasource:
    url: jdbc:postgresql://${PG_HOST:localhost/postgres}
    username: ${PG_USERNAME:postgres}
    password: ${PG_PASSWORD:postgres}
  jackson:
    serialization:
      write-dates-as-timestamps: false
      write-durations-as-timestamps: false
api:
  data:
    config:
      default:
        resultLimit: 10
  media:
    config:
      baseUrl: ${MEDIA_URL:http://localhost:8888/media}

Gradle依赖

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa") {
        exclude(group = "ch.qos.logback", module = "logback-classic")
    }
    implementation("org.springframework.boot:spring-boot-starter-jdbc")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-websocket")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("com.fasterxml.jackson.module:jackson-module-parameter-names")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
//    implementation("com.fasterxml.jackson.datatype:jackson-module-kotlin")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    // OpenAPI
    implementation("com.squareup.okhttp3:okhttp:4.11.0")
    implementation("org.openapitools:openapi-generator-gradle-plugin:6.6.0")


    compileOnly("org.projectlombok:lombok")
    runtimeOnly("org.postgresql:postgresql")
    annotationProcessor("org.projectlombok:lombok")
    itestImplementation("org.springframework.boot:spring-boot-starter-test")
    itestImplementation("org.testcontainers:junit-jupiter")
    itestImplementation("org.testcontainers:postgresql")
    itestImplementation("io.rest-assured:rest-assured:5.3.1")
    itestImplementation("org.testcontainers:testcontainers:1.18.3")
    itestImplementation("org.testcontainers:postgresql:1.18.3")
//    itestImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")
}

调试发现

调试Spring Boot的InstantSerializerBase类时发现,默认序列化器中WRITE_DATE_AS_TIMESTAMPS和WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS特性仍处于启用状态,尽管已在应用属性中配置关闭这些特性。尝试清理构建、重新克隆仓库均未解决问题。

解决方案

1. 移除@EnableWebMvc或手动配置Jackson

使用@EnableWebMvc会禁用Spring Boot自动配置的Jackson设置,导致application.yml中的jackson配置不生效。可以:

  • 方案A:移除@EnableWebMvc
    直接删掉配置类上的@EnableWebMvc注解,Spring Boot会自动应用Jackson的配置。

  • 方案B:保留@EnableWebMvc并手动配置Jackson
    在配置类中添加Jackson2ObjectMapperBuilder的Bean,手动指定序列化特性:

    @Configuration
    @EnableWebMvc
    class WebConfiguration : WebMvcConfigurer {
        override fun addCorsMappings(registry: CorsRegistry) {
            registry.addMapping("/**")
        }
    
        @Bean
        fun jackson2ObjectMapperBuilder(): Jackson2ObjectMapperBuilder {
            return Jackson2ObjectMapperBuilder()
                .featuresToDisable(
                    SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
                    SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS
                )
                .modulesToInstall(
                    JavaTimeModule(),
                    KotlinModule(),
                    Jdk8Module(),
                    ParameterNamesModule()
                )
        }
    }
    

2. 配置OpenAPI生成器使用Java8日期库

确保OpenAPI生成器生成的类使用正确的日期序列化逻辑,在Gradle的OpenAPI生成配置中指定dateLibrary为java8:

openApiGenerate {
    // 其他配置(如spec文件路径等)
    configOptions = [
        dateLibrary: "java8"
    ]
}

3. 检查并解决Jackson依赖冲突

确保所有Jackson相关依赖版本与Spring Boot版本一致,避免因版本不一致导致的配置失效。可以通过Gradle的dependencyInsight命令检查:

./gradlew dependencyInsight --dependency jackson-datatype-jsr310

4. 手动注册JavaTimeModule并禁用时间戳序列化

如果自动配置仍未生效,手动注册JavaTimeModule并禁用相关特性:

@Configuration
class JacksonConfig {
    @Bean
    fun objectMapper(): ObjectMapper {
        val mapper = ObjectMapper()
        val javaTimeModule = JavaTimeModule()
        mapper.registerModule(javaTimeModule)
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        mapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
        return mapper
    }
}

内容的提问来源于stack exchange,提问作者Oscar Reyes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:56:06