保留JSON中的$schema元数据时解决Jackson ObjectMapper的MismatchedInputException异常问题
保留JSON中的$schema元数据时解决Jackson ObjectMapper的MismatchedInputException异常问题
嗨,这个问题我之前也碰到过!你配置的@JsonIgnoreProperties(ignoreUnknown = true)和FAIL_ON_UNKNOWN_PROPERTIES = false之所以没用,核心原因是Jackson处理EnumMap的逻辑和普通Java对象不一样:
当你反序列化EnumMap<TestCaseEnum, MailingRequest>时,Jackson会把JSON里的每一个键都尝试转换成TestCaseEnum枚举的有效值,而$schema显然不在你的枚举选项里,所以它会直接抛出MismatchedInputException——这个时候Jackson并不是把$schema当成MailingRequest的未知属性,而是当成EnumMap的键来解析,所以那些忽略未知属性的配置根本不会生效。
下面给你两种实用的解决方案:
方案一:先用HashMap接收,再转成EnumMap
这种方法最简单直接,先用HashMap容纳所有JSON键值对,再手动把属于枚举的键值对提取到EnumMap中,非枚举键(比如$schema)可以直接忽略:
ObjectMapper objectMapper = new ObjectMapper(); // 第一步:把JSON反序列化成HashMap,容纳所有键值对 HashMap<String, Object> tempMap = objectMapper.readValue( MailingRequestFactory.class.getResource("/test-data.json"), new TypeReference<HashMap<String, Object>>() {} ); // 第二步:提取枚举相关的键值对到EnumMap EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = new EnumMap<>(TestCaseEnum.class); for (Map.Entry<String, Object> entry : tempMap.entrySet()) { try { // 尝试把键转成枚举值 TestCaseEnum enumKey = TestCaseEnum.valueOf(entry.getKey()); // 把对应的值转成MailingRequest对象 MailingRequest request = objectMapper.convertValue(entry.getValue(), MailingRequest.class); mailingRequestCache.put(enumKey, request); } catch (IllegalArgumentException e) { // 碰到非枚举键(比如$schema)时直接忽略,也可以在这里做其他处理 System.out.println("自动忽略非枚举键:" + entry.getKey()); } }
方案二:自定义EnumMap反序列化器
如果不想每次都手动转换,可以写一个自定义的反序列化器,让Jackson自动忽略非枚举键:
第一步:实现自定义反序列化器
public class IgnoringEnumMapDeserializer<K extends Enum<K>, V> extends StdDeserializer<EnumMap<K, V>> { private final Class<K> enumClass; private final Class<V> valueClass; public IgnoringEnumMapDeserializer(Class<K> enumClass, Class<V> valueClass) { super(EnumMap.class); this.enumClass = enumClass; this.valueClass = valueClass; } @Override public EnumMap<K, V> deserialize(JsonParser parser, DeserializationContext context) throws IOException { EnumMap<K, V> enumMap = new EnumMap<>(enumClass); JsonNode rootNode = parser.getCodec().readTree(parser); // 遍历所有JSON键值对 for (Iterator<Map.Entry<String, JsonNode>> iterator = rootNode.fields(); iterator.hasNext(); ) { Map.Entry<String, JsonNode> entry = iterator.next(); String keyStr = entry.getKey(); try { // 尝试转换为枚举键 K enumKey = Enum.valueOf(enumClass, keyStr); // 转换值为目标对象 V value = parser.getCodec().treeToValue(entry.getValue(), valueClass); enumMap.put(enumKey, value); } catch (IllegalArgumentException e) { // 忽略无法转换为枚举的键 continue; } } return enumMap; } }
第二步:注册反序列化器并使用
ObjectMapper objectMapper = new ObjectMapper(); SimpleModule customModule = new SimpleModule(); // 注册针对你的EnumMap类型的反序列化器 customModule.addDeserializer( new TypeReference<EnumMap<TestCaseEnum, MailingRequest>>() {}, new IgnoringEnumMapDeserializer<>(TestCaseEnum.class, MailingRequest.class) ); objectMapper.registerModule(customModule); // 现在可以直接反序列化了,$schema会被自动忽略 EnumMap<TestCaseEnum, MailingRequest> mailingRequestCache = objectMapper.readValue( MailingRequestFactory.class.getResource("/test-data.json"), new TypeReference<EnumMap<TestCaseEnum, MailingRequest>>() {} );
两种方案都能解决问题,第一种适合快速实现,第二种适合需要多次使用的场景,你可以根据自己的需求选择~
备注:内容来源于stack exchange,提问作者grappler
相关产品推荐
相关产品推荐

