如何修改集合反序列化器实现条件过滤及异常容错?
解决方案
针对你遇到的Jackson集合反序列化时无法自动过滤无效Link、以及希望具备异常容错性的问题,这里有两种针对性的解决方案,完全符合你不想编写专门JsonDeserializer<Collection<Link>>且要全局遵循Link#valid规则的要求:
问题1:修改集合反序列化器生成过滤后的集合
方案1:利用Link反序列化器返回null + 全局过滤null元素
这是最简洁的方案,不需要修改集合反序列化逻辑,仅通过调整LinkDeserialiser和Jackson配置实现全局过滤:
- 修改
LinkDeserialiser,在反序列化完成后校验Link#valid(),不满足则返回null:
@Override public Link deserialize(JsonParser parser, DeserializationContext context) throws IOException { final JsonNode node = context.readValue(parser, JsonNode.class); Link link = Link.builder() .title(node.path(Constants.TITLE).asText().trim()) .href(node.path(Constants.HREF).asText().trim()) .links(loadLinks(node.path(Constants.LINKS))) .build(); // 验证不通过则返回null,后续由Jackson自动过滤 return link.valid() ? link : null; }
- 在你的
ObjectMapper中启用DeserializationFeature.FILTER_NULL_VALUES,让集合反序列化时自动跳过null元素:
ObjectMapper mapper = new ObjectMapper(); // 启用null元素过滤 mapper.configure(DeserializationFeature.FILTER_NULL_VALUES, true); // 注册自定义Link反序列化器 mapper.registerModule(new SimpleModule().addDeserializer(Link.class, new LinkDeserialiser()));
这个方案的优势在于:
- 完全复用Jackson默认的集合反序列化逻辑,不需要编写专门的
Collection<Link>反序列化器 - 全局遵循
Link#valid规则,不管是单个Link还是集合中的Link,都会被自动校验过滤 - 调用方无需任何额外处理,反序列化后的集合直接是有效Link的集合
方案2:继承CollectionDeserializer自定义元素过滤(不依赖null)
如果你不想依赖null值过滤,可以通过继承Jackson的CollectionDeserializer,在元素添加到集合前做校验:
- 创建自定义的
FilteringCollectionDeserializer,重写addItem方法实现过滤:
public class FilteringCollectionDeserializer extends CollectionDeserializer { public FilteringCollectionDeserializer(CollectionDeserializer src) { super(src); } @Override protected void addItem(DeserializationContext ctxt, Collection<Object> result, Object item) throws IOException { // 仅当元素是Link且有效时,才添加到集合中 if (item instanceof Link && ((Link) item).valid()) { super.addItem(ctxt, result, item); } } // 重写createContextual方法,确保上下文参数正确传递 @Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { CollectionDeserializer contextualDeserializer = (CollectionDeserializer) super.createContextual(ctxt, property); return new FilteringCollectionDeserializer(contextualDeserializer); } }
- 通过
BeanDeserializerModifier注册这个自定义集合反序列化器,仅对Link类型的集合生效:
ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); // 注册Link反序列化器 module.addDeserializer(Link.class, new LinkDeserialiser()); // 注册自定义集合过滤逻辑 module.setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { // 仅处理元素类型为Link的集合 if (type.getContentType().getRawClass() == Link.class) { return new FilteringCollectionDeserializer((CollectionDeserializer) deserializer); } return super.modifyCollectionDeserializer(config, type, beanDesc, deserializer); } }); mapper.registerModule(module);
问题2:让集合反序列化器具备异常容错性(抛出异常时继续收集有效对象)
要实现元素反序列化失败时不中断整个集合的处理,你可以在LinkDeserialiser中捕获反序列化异常,返回null,再结合上面的FILTER_NULL_VALUES配置,自动跳过异常元素:
修改LinkDeserialiser,添加异常捕获逻辑:
@Override public Link deserialize(JsonParser parser, DeserializationContext context) throws IOException { try { final JsonNode node = context.readValue(parser, JsonNode.class); Link link = Link.builder() .title(node.path(Constants.TITLE).asText().trim()) .href(node.path(Constants.HREF).asText().trim()) .links(loadLinks(node.path(Constants.LINKS))) .build(); return link.valid() ? link : null; } catch (Exception e) { // 捕获任何反序列化过程中的异常(比如字段缺失、类型不匹配等),返回null // 你也可以在这里记录异常日志,方便排查问题 return null; } }
同时保持ObjectMapper的FILTER_NULL_VALUES配置,这样不管是无效的Link还是反序列化失败的Link,都会被自动跳过,集合中只保留有效且反序列化成功的元素。
内容的提问来源于stack exchange,提问作者Andrew




