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

Spring Boot RestTemplate映射JSON的空值与动态键反序列化问题

HasOffers findAll接口反序列化异常解决方案

核心问题1:大量字段映射为null的排查&修复

按优先级逐个排查:

  • 层级不匹配:HasOffers接口返回结构存在固定多层嵌套,api_key在最外层request节点下,status/httpStatus/data都在response节点下,如果直接把这些字段平铺定义在最外层响应类,除了刚好位置对的几个字段,其余全为null。
  • 命名策略不匹配:接口返回字段默认是下划线命名(如offer_id、default_payout),Spring Boot内置Jackson默认用驼峰命名匹配,要么给字段加@JsonProperty("原字段名")指定映射关系,要么全局配置Jackson命名策略为下划线转驼峰。
  • 缺少Setter方法:Jackson反序列化默认依赖public修饰的setter方法给字段赋值,如果实体类字段是私有属性、没写setter也没加Lombok@Data注解自动生成方法,字段无法被赋值。

核心问题2:动态key的data节点映射方案

HasOffers类接口的data节点不是标准JSON数组,是以资源ID为动态key、单条资源为value的对象结构,不要定义为List<Offer>接收,直接用JDK自带的Map<String, 单条资源包装类>接收即可,Jackson会自动完成动态键值对的反序列化。
注意:data下每个单条offer的内容还会包裹在固定key为Offer的子对象中,需要单独定义包装层承接。

可直接复用的代码实现

1. 响应实体类定义

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

@Data
public class HasOffersApiResponse {
    private RequestInfo request;
    private ResponseBody response;

    @Data
    public static class RequestInfo {
        @JsonProperty("api_key")
        private String apiKey;
    }

    @Data
    public static class ResponseBody {
        private Integer status;
        private Integer httpStatus;
        private List<Object> errors;
        // 动态key节点直接用Map接收,key为字符串类型的offerID
        private Map<String, OfferEntry> data;
    }

    @Data
    public static class OfferEntry {
        // 承接固定key为Offer的子节点
        @JsonProperty("Offer")
        private Offer offer;
    }

    @Data
    public static class Offer {
        private String id;
        private String name;
        private BigDecimal defaultPayout;
        private String previewUrl;
        // 其余字段按照接口文档的返回key逐个补充即可
    }
}

2. 全局Jackson配置(可选,省掉逐个写字段映射注解)

在application.yml中添加配置,自动完成下划线命名字段到驼峰实体字段的映射:

spring:
  jackson:
    property-naming-strategy: SNAKE_CASE

3. RestTemplate调用逻辑

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class OfferService {
    @Resource
    private RestTemplate restTemplate;

    public List<Offer> fetchAllOffers() {
        String reqUrl = "https://你的HasOffers接口地址/Offer/findAll?NetworkId=你的ID&api_key=你的密钥";
        ResponseEntity<HasOffersApiResponse> resp = restTemplate.getForEntity(reqUrl, HasOffersApiResponse.class);
        HasOffersApiResponse body = resp.getBody();
        // 从动态Map中提取所有offer实体,转成普通List使用
        return body.getResponse().getData().values()
                .stream()
                .map(HasOffersApiResponse.OfferEntry::getOffer)
                .collect(Collectors.toList());
    }
}

验证注意点

  • 先拿接口原始返回的JSON片段,对照实体类层级逐层核对,不要凭感觉写实体结构。
  • 如果个别字段还是为null,直接打印原始响应字符串,确认接口本身是否返回了对应字段,不要默认所有字段接口都会返回。

内容的提问来源于stack exchange,提问作者Gabriel Vendramini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:24:20