JSON反序列化后如何去除空DTO对象?求替代list.get(1)的方案
解决JSON数组反序列化时无效空对象的问题
问题背景
原JSON返回的是包含两个元素的数组:第一个元素仅含charsetinfo元数据字段,第二个元素是包含engine/timetable/dailytable的业务数据。通过FeignClient反序列化为List<SingleEngineJsonDto>后,列表第一个元素因@JsonIgnoreProperties忽略了charsetinfo,导致所有业务字段为null;第二个元素才是有效数据。当前硬编码list.get(1)获取有效数据的方式被禁止,需要找到可靠的处理方案。
解决方案
方案1:自定义Jackson反序列化器(推荐)
直接在序列化阶段过滤掉仅含charsetinfo的无效对象,让返回的列表只保留有效业务数据。
- 编写自定义反序列化器:
public class FilteredSingleEngineJsonDtoListDeserializer extends JsonDeserializer<List<SingleEngineJsonDto>> { @Override public List<SingleEngineJsonDto> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { ObjectMapper mapper = (ObjectMapper) p.getCodec(); JsonNode rootNode = mapper.readTree(p); List<SingleEngineJsonDto> validDtos = new ArrayList<>(); for (JsonNode node : rootNode) { // 仅保留包含业务字段的对象 if (node.has("engine") || node.has("timetable") || node.has("dailytable")) { SingleEngineJsonDto dto = mapper.treeToValue(node, SingleEngineJsonDto.class); validDtos.add(dto); } } return validDtos; } }
- 在FeignClient的方法上指定该反序列化器:
@FeignClient(value = "engine", url = "engine") public interface EngineClient { // ...其他方法 @GetMapping @JsonDeserialize(using = FilteredSingleEngineJsonDtoListDeserializer.class) List<SingleEngineJsonDto> getEngine(URI engineUri, @RequestParam(value = "lang", required = false) String lang); }
这样反序列化后的列表只会包含有效数据,直接用list.get(0)即可获取目标对象。
方案2:业务层过滤无效对象(快速实现)
如果不想修改序列化逻辑,可在服务层对列表进行过滤,筛选出存在非null业务字段的对象:
@Service public class EngineParamEngine implements EngineFactoryInterface { @Override public List<SingleEngineDTO> getEngineObjectPart(List<SingleEngineJsonDto> list){ return list.stream() .filter(dto -> dto.getEngine() != null || dto.getTimetable() != null || dto.getDailytable() != null) .findFirst() .orElseThrow(() -> new RuntimeException("未找到有效引擎数据")) .getEngine(); } }
该方案无需改动序列化配置,通过流式处理过滤无效对象,避免了硬编码索引的风险。
方案3:调整DTO结构适配原JSON
若需要保留charsetinfo元数据,可定义顶层DTO统一接收数组中的两种对象:
// 顶层响应DTO @Getter @Setter @ToString public class EngineResponse { private CharsetInfo charsetinfo; private List<SingleEngineDTO> engine; private List<Timetable> timetable; private List<Dailytable> dailytable; } // 字符集信息类 @Getter @Setter public class CharsetInfo { private String name; }
修改FeignClient返回类型为List<EngineResponse>,之后在业务层筛选出包含业务字段的对象即可。不过该方案仅在需要使用charsetinfo时适用,否则冗余。
注意事项
- 确保DTO字段与JSON字段名匹配:若JSON中是大写的
NAME,DTO中需用@JsonProperty("NAME")标注对应字段,否则会导致映射失败。 - 自定义反序列化器需确保FeignClient使用的Jackson配置能识别该注解,若使用Spring Boot默认配置则无需额外注册。
内容的提问来源于stack exchange,提问作者SoN
相关产品推荐
相关产品推荐

