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
相关产品推荐
相关产品推荐

