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

如何通过Feign客户端映射嵌套JSON中的List对象?

解决Feign调用API时嵌套List的反序列化问题

错误原因

你遇到的Cannot deserialize value of type java.util.ArrayList<> from Object value错误,核心原因是:

  • API返回的是完整的JSON对象(顶层包含apiVersion、transactionId、data等字段)
  • 但你的Feign方法直接声明返回List<AgendamentoResponse>,Jackson尝试将整个顶层JSON对象转换为List,类型不匹配导致失败
  • 同时你原来的@JsonSetter路径data.workschedule.scheduleDate无效,因为workschedule是一个List,而非单个对象,无法通过该路径定位到列表元素的字段

最优解决方案:定义顶层响应DTO

最直接且易维护的方式是创建一个顶层DTO,映射API返回的完整结构,再从中提取需要的List。

1. 创建顶层响应DTO

public record AgendamentoApiResponse(
    String apiVersion,
    String transactionId,
    Data data
) {
    // 嵌套内部类,映射data字段的结构
    public record Data(
        List<AgendamentoResponse> workschedule
    ) {}
}

2. 调整AgendamentoResponse的字段映射

去掉原来路径中的data.workschedule.,直接映射列表元素的字段:

public record AgendamentoResponse(
    @DatePattern
    @JsonSetter("scheduleDate")
    LocalDate dataAgendamento,
    @JsonSetter("timeslotDescription")
    String periodoLabel,
    @JsonSetter("timeslotStart")
    String periodoInicio,
    @JsonSetter("timeslotEnd")
    String periodoFim,
    @JsonSetter("category")
    String categoria
) {}

3. 修改Feign客户端的返回类型

将返回类型改为顶层DTO,之后从DTO中提取List:

@GetMapping("appointments/workschedules")
AgendamentoApiResponse consultarAgendamentosDisponiveis(
    @RequestHeader(name = "X-QueryString", required = false) String partyAccountId,
    @SpringQueryMap AgendamentoParams params);

4. 调用示例

// 调用Feign接口
AgendamentoApiResponse apiResponse = feignClient.consultarAgendamentosDisponiveis(partyAccountId, params);
// 提取需要的List
List<AgendamentoResponse> agendamentos = apiResponse.data().workschedule();

可选方案:自定义Feign Decoder(进阶)

如果不想创建顶层DTO,可以自定义Decoder,直接从JSON中提取data.workschedule路径下的List:

@Component
public class CustomFeignDecoder extends SpringDecoder {
    private final ObjectMapper objectMapper;

    public CustomFeignDecoder(ObjectMapper objectMapper) {
        super(new ObjectFactory<HttpMessageConverter<?>>() {
            @Override
            public HttpMessageConverter<?> getObject() throws BeansException {
                MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
                converter.setObjectMapper(objectMapper);
                return converter;
            }
        });
        this.objectMapper = objectMapper;
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        if (type instanceof ParameterizedType parameterizedType &&
            parameterizedType.getRawType().equals(List.class)) {
            // 直接读取data.workschedule路径下的List
            JsonNode rootNode = objectMapper.readTree(response.body().asInputStream());
            JsonNode workscheduleNode = rootNode.path("data").path("workschedule");
            return objectMapper.treeToValue(workscheduleNode, type);
        }
        return super.decode(response, type);
    }
}

然后在Feign客户端配置中指定这个Decoder:

@FeignClient(name = "agendamentoClient", url = "${api.url}", configuration = {CustomFeignDecoder.class})
public interface AgendamentoFeignClient {
    // 方法保持原来的List返回类型
    @GetMapping("appointments/workschedules")
    List<AgendamentoResponse> consultarAgendamentosDisponiveis(
        @RequestHeader(name = "X-QueryString", required = false) String partyAccountId,
        @SpringQueryMap AgendamentoParams params);
}

注意:这种方式适合需要频繁跳过顶层字段的场景,但会增加代码复杂度,建议优先使用顶层DTO方案。

内容的提问来源于stack exchange,提问作者João Victor Afonso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:05:57