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

如何用Jackson反序列化器解析JAX-RS Response中的Spring Data Page

通用解决Spring Data Page Jackson反序列化问题

问题核心

自定义Jackson Page反序列化器时,createContextual方法中wrapperType.containedType(0)返回null,无法自动识别content的泛型类型,导致无法通用解析不同类型的content(如EtudeDto、EtudeOptique)。

解决方案

1. 修正泛型类型获取逻辑

调整反序列化器中获取泛型参数的方式,从BeanProperty或上下文JavaType中正确提取content的类型:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.io.IOException;
import java.util.List;

public class PageDeserializer extends JsonDeserializer<Page<?>> implements ContextualDeserializer {

    private JavaType contentJavaType;

    @Override
    public Page<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        JsonNode contentNode = node.get("content");
        // 用预获取的泛型类型解析content列表
        List<?> content = ctxt.readValue(contentNode.traverse(p.getCodec()), contentJavaType);
        
        int pageNumber = node.get("number").asInt();
        int pageSize = node.get("size").asInt();
        long totalElements = node.get("totalElements").asLong();

        return new PageImpl<>(content, PageRequest.of(pageNumber, pageSize), totalElements);
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        PageDeserializer deserializer = new PageDeserializer();
        // 优先从BeanProperty提取泛型
        if (property != null) {
            JavaType wrapperType = property.getType();
            deserializer.contentJavaType = wrapperType.containedType(0);
        } else {
            // 无BeanProperty时,从上下文获取当前解析的泛型类型
            JavaType currentType = ctxt.getContextualType();
            if (currentType != null && currentType.isContainerType()) {
                deserializer.contentJavaType = currentType.containedType(0);
            }
        }
        // 兜底校验,避免空指针
        if (deserializer.contentJavaType == null) {
            throw new JsonMappingException(null, "无法识别Page的泛型类型,请确保反序列化时指定具体的Page<XXX>类型");
        }
        return deserializer;
    }
}

2. 注册反序列化器并明确泛型类型

解析时必须通过TypeReference指定具体的Page<XXX>类型,不能直接用Page.class:

ObjectMapper objectMapper = new ObjectMapper();
// 注册自定义反序列化器
SimpleModule module = new SimpleModule();
module.addDeserializer(Page.class, new PageDeserializer());
objectMapper.registerModule(module);

// 解析为Page<EtudeDto>
String json = "..."; // 你的JSON响应内容
Page<EtudeDto> etudePage = objectMapper.readValue(json, new TypeReference<Page<EtudeDto>>() {});

// 解析为Page<EtudeOptique>
Page<EtudeOptique> optiquePage = objectMapper.readValue(json, new TypeReference<Page<EtudeOptique>>() {});

3. 适配标准Page JSON结构

确保Web服务返回的JSON符合Spring Data Page的标准格式,示例:

{
  "content": [
    {"id": 1, "name": "示例EtudeDto"},
    {"id": 2, "name": "示例EtudeDto2"}
  ],
  "number": 0,
  "size": 2,
  "totalElements": 5,
  "totalPages": 3,
  "first": true,
  "last": false,
  "empty": false
}

关键注意点

  • 必须使用TypeReference传递具体泛型,Jackson才能在createContextual中识别content类型。
  • 如果JSON中content字段名不是content,需在反序列化器中修改node.get("content")的字段名。
  • 兜底异常用于快速定位未指定泛型的错误场景。

内容的提问来源于stack exchange,提问作者Robyn.D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 12:18:28