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

Redis缓存JSON反序列化为Java对象失败问题求助

Redis缓存字符串反序列化为Java对象失败问题

问题背景

通过@Cacheable注解将BusinessPartyBasicResponse对象缓存到Redis后,缓存的JSON格式如下:

"{\"@class\":\"org.BusinessPartyBasicResponse\",\"OutputParameters\":{\"@class\":\"org.BusinessPartyBasicResponse$OutputParameters\",\"X_PARTY_DATA_TABLE\":{\"@class\":\"org.BusinessPartyBasicResponse$OutputParameters$PartyDataTable\",\"X_PARTY_DATA_TABLE_ITEM\":[\"java.util.ArrayList\",[{\"@class\":\"org.BusinessPartyBasicResponse$OutputParameters$PartyDataTable$PartyDataTableItem\",\"TYPECODE\":\"AC\",\"ENTERPRISEID\":\"C4C089BBDD857CDCE0531A1E970A0733\",\"NAME\":\"TEST_NAME CENTER\",\"DOINGBUSINESSASNAME\":null,\"IDENTIFIER\":\"1234\",\"ION\":null,\"COUNTRYCODE\":\"US\",\"STATUS\":\"A\",\"SUPPLIEDSOURCES\":\" \",\"CONTRACT_STATUS\":null,\"OFAC\":null},null]]},\"X_BP_COUNT\":1,\"X_RETURN_STATUS\":\"S\"}}"

使用继承自GenericJackson2JsonRedisSerializer的自定义序列化器,无法修改JSON字段名和实体类的@JsonProperty注解。反序列化代码如下:

public BusinessPartyBasicResponse findByType(BpType bpType) {
    log.info("Find BPs with type=[{}]", bpType);
    Set<String> allBp = ops.keys("common:businessPartyBasic::*");
    BusinessPartyBasicResponse response = new BusinessPartyBasicResponse();
    String bpJson = null;
    for (String bp : allBp) {
        bpJson = ops.opsForValue().get(bp);
        if (bpJson.contains("\"TYPECODE\":\"" + bpType.name().toUpperCase() + "\"")) {
            log.info("bpJson {}", bpJson);
            BusinessPartyBasicResponse basicResponse = null;
            try {
                basicResponse = mapper.readValue(bpJson, BusinessPartyBasicResponse.class);
                response.getOutputParameters().setResultsCount(
                        response.getOutputParameters().getResultsCount()
                                + basicResponse.getOutputParameters().getResultsCount());
                response.getPartyDataTableItem().addAll(basicResponse.getPartyDataTableItem());
            } catch (JsonProcessingException e) {
                log.info("error processing json: {}" ,e);
                return null;
            }
        }
    }
    return response;
}

抛出错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.BusinessPartyBasicResponse$OutputParameters$PartyDataTable$PartyDataTableItem` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('java.util.ArrayList')
...

错误原因

缓存的JSON中,X_PARTY_DATA_TABLE_ITEM采用了Jackson多态数组格式(["java.util.ArrayList", [元素列表]]),但默认的List反序列化器无法识别这种结构,会将数组第一个元素"java.util.ArrayList"当作PartyDataTableItem的字符串值解析,导致找不到匹配的构造方法,抛出异常。

解决方案

方案1:复用缓存序列化器的ObjectMapper

缓存是由继承自GenericJackson2JsonRedisSerializer的序列化器生成的,该序列化器的ObjectMapper已经配置了支持@class这类多态标识的解析逻辑。直接使用这个ObjectMapper进行反序列化:

// 假设你的自定义Redis序列化器实例为customRedisSerializer
ObjectMapper redisMapper = customRedisSerializer.getObjectMapper();
basicResponse = redisMapper.readValue(bpJson, BusinessPartyBasicResponse.class);

方案2:配置自定义ObjectMapper支持多态格式

如果必须使用独立的ObjectMapper,需要启用默认类型处理,匹配GenericJackson2JsonRedisSerializer的序列化规则:

ObjectMapper mapper = new ObjectMapper();
// 启用默认类型推断,对应GenericJackson2JsonRedisSerializer的默认配置
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// 保留原有的忽略未知字段配置
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

方案3:自定义List反序列化器处理特殊数组

如果上述方案无法实施,编写自定义反序列化器处理X_PARTY_DATA_TABLE_ITEM的特殊格式:

public class CustomListDeserializer extends JsonDeserializer<List<BusinessPartyBasicResponse.OutputParameters.PartyDataTable.PartyDataTableItem>> {
    @Override
    public List<BusinessPartyBasicResponse.OutputParameters.PartyDataTable.PartyDataTableItem> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        if (node.isArray() && node.size() == 2) {
            JsonNode dataNode = node.get(1);
            if (dataNode.isArray()) {
                ObjectMapper mapper = (ObjectMapper) p.getCodec();
                List<BusinessPartyBasicResponse.OutputParameters.PartyDataTable.PartyDataTableItem> items = 
                    mapper.readValue(dataNode.traverse(), new TypeReference<>(){});
                // 过滤列表中的null元素
                return items.stream().filter(Objects::nonNull).collect(Collectors.toList());
            }
        }
        // 兼容普通数组格式
        return ctxt.readValue(p, new TypeReference<>(){});
    }
}

然后在实体类的对应字段上添加该反序列化器:

@JsonProperty("X_PARTY_DATA_TABLE_ITEM")
@JsonDeserialize(using = CustomListDeserializer.class)
private List<PartyDataTableItem> partyDataTableItem = new ArrayList<>();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 15:02:03