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

WebClient自定义ObjectMapper解决Instant反序列化失败问题

解决Spring Boot WebClient调用时Instant类型JSON解码失败问题

看起来你遇到的是WebClient默认Jackson配置不识别自定义日期格式的问题——虽然实体类加了@JsonFormat,但WebClient自带的ObjectMapper并没有用上这个配置,导致无法把"Feb 19, 2020 5:30:00 PM"这种格式的字符串反序列化为Instant。下面我一步步给你解决思路和代码示例:

1. 先确认实体类的注解配置是否正确

首先检查你的TalonInfo类,确保registrationTime字段的@JsonFormat格式完全匹配接口返回的日期字符串,还要指定时区和本地化(因为是英文月份缩写):

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.Instant;

public class TalonInfo {
    // 其他字段...
    
    @JsonFormat(
        shape = JsonFormat.Shape.STRING,
        pattern = "MMM dd, yyyy hh:mm:ss a",
        locale = "en_US",
        timezone = "GMT" // 这里要和接口返回的日期时区一致
    )
    private Instant registrationTime;

    // getter、setter方法
}

2. 自定义WebClient的ObjectMapper配置

WebClient默认的编解码器不会自动复用你项目中自定义的ObjectMapper,所以我们需要手动替换它的Jackson编解码器,让它使用能识别这个日期格式的配置。

方式一:创建独立的自定义ObjectMapper并注入WebClient

写一个配置类,专门为WebClient定制ObjectMapper:

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;

import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

@Configuration
public class WebClientConfig {

    @Bean
    public ObjectMapper webClientObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 注册Java 8时间类型处理模块
        objectMapper.registerModule(new JavaTimeModule());
        // 启用注解支持,让实体类的@JsonFormat生效
        objectMapper.enable(com.fasterxml.jackson.databind.MapperFeature.USE_ANNOTATIONS);
        
        // 额外添加Instant的自定义反序列化器(如果@JsonFormat不生效的话可以用这个兜底)
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Instant.class, new JsonDeserializer<Instant>() {
            @Override
            public Instant deserialize(com.fasterxml.jackson.core.JsonParser p, DeserializationContext ctxt) throws IOException {
                String dateStr = p.getText();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd, yyyy hh:mm:ss a")
                        .withLocale(Locale.ENGLISH)
                        .withZone(ZoneId.of("GMT"));
                return Instant.from(formatter.parse(dateStr));
            }
        });
        objectMapper.registerModule(module);
        
        return objectMapper;
    }

    @Bean
    public WebClient customWebClient(WebClient.Builder webClientBuilder, ObjectMapper webClientObjectMapper) {
        // 构建自定义的ExchangeStrategies,替换默认的Jackson编解码器
        ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> {
                    configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(webClientObjectMapper));
                    configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(webClientObjectMapper));
                })
                .build();

        return webClientBuilder
                .exchangeStrategies(strategies)
                .baseUrl("你的接口基础地址") // 替换成实际的接口域名/IP
                .build();
    }
}

方式二:复用Spring Boot自动配置的ObjectMapper

如果你项目中已经有全局的ObjectMapper配置,可以直接复用它,只需要修改WebClient的编解码器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Autowired
    private ObjectMapper objectMapper;

    @Bean
    public WebClient customWebClient(WebClient.Builder webClientBuilder) {
        ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> {
                    configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
                    configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
                })
                .build();

        return webClientBuilder
                .exchangeStrategies(strategies)
                .baseUrl("你的接口基础地址")
                .build();
    }
}

这种方式需要确保你的全局ObjectMapper已经注册了JavaTimeModule并启用了注解支持。

3. 在服务类中使用自定义的WebClient

修改你的服务实现类,注入我们配置好的WebClient:

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
public class TalonInfoService {

    private final WebClient webClient;

    // 构造注入自定义WebClient
    public TalonInfoService(WebClient customWebClient) {
        this.webClient = customWebClient;
    }

    public TalonInfo getInfo() {
        return webClient.get()
                .uri("/getinfo") // 接口的具体路径
                .retrieve()
                .bodyToMono(TalonInfo.class)
                .block(); // 现在执行block()应该不会再出现解码错误了
    }
}

4. 测试类示例

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
public class TalonInfoServiceTest {

    @Autowired
    private TalonInfoService talonInfoService;

    @Test
    public void testGetInfoSuccess() {
        TalonInfo talonInfo = talonInfoService.getInfo();
        assertNotNull(talonInfo);
        assertNotNull(talonInfo.getRegistrationTime());
        System.out.println("解析后的时间:" + talonInfo.getRegistrationTime());
    }
}

补充:待反序列化的JSON示例

确保接口返回的JSON格式如下(和你的实体类字段对应):

{
  "id": 123,
  "registrationTime": "Feb 19, 2020 5:30:00 PM",
  "name": "test-talon"
}

内容的提问来源于stack exchange,提问作者Анатолий Оглоблин

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 09:12:54