Jackson中基于上下文拦截属性输出的两种实现方式

栏目: Java · 发布时间: 6年前

内容简介:需求如下,在一个类中,有一些字段属性,其是否输出并不是由字段上的JsonIgnore来决定,而是根据从上下文(如request)中传递过来的某些参数决定。如下类:当上下文值为 field1 时,则表示 A 的最终输出json 中没有字段 field1. 当上下文为 field2 时, 则最终输出没有字段 field2. 其它情况则输出所有字段。本文讨论Jackson中的处理方式,如果使用 Fastjson,则有很多方式处理,这里不表.

需求如下,在一个类中,有一些字段属性,其是否输出并不是由字段上的JsonIgnore来决定,而是根据从上下文(如request)中传递过来的某些参数决定。如下类:

class A {
    @ContextIgnored("field1")
    private String field1;

    @ContextIgnored("field2")
    private String field2;
}

当上下文值为 field1 时,则表示 A 的最终输出json 中没有字段 field1. 当上下文为 field2 时, 则最终输出没有字段 field2. 其它情况则输出所有字段。

本文讨论Jackson中的处理方式,如果使用 Fastjson,则有很多方式处理,这里不表.

本文描述通过利用 JsonIgnore 注解处理方式解决此问题 和 通过 PropertyFilter 两种方式来完成此需求的处理方式。

利用 AnnotationIntrospector 中 hasIgnoreMarker 实现属性过滤

在标准的类处理中,jackson在处理每个类时,均会为此类生成1个 JsonSerializer 对象,在此场景中应该为 BeanSerializer 对象,而类中属性的处理,则是由 BeanPropertyWriter 来完成。即可以理解为在处理对象时,会先有1个解析类和属性的过程存在,如果在此过程中,检测到某个属性不应该最终输出,则不会产生相应属性的 BeanPropertyWriter 对象,即达到过滤输出的目的。

在正常的场景中,我们可以通过 注解 JsonIgnore, 将其加到属性上,即解析时即会过滤到属性。而实际实现,则是由类 JacksonAnnotationIntrospector 中 的 hasIgnoreMarker 来完成,则就是通过读取注解来判断属性是否应该被exclude掉。ObjectMapper中默认的 AnnotationIntrospector 即是 JacksonAnnotationIntrospector 来完成,但我们可以通过 方法 ObjectMapper.setAnnotationIntrospector 来重新指定自定义的实现。如下参考代码.

class VerifyIntrospetor extends JacksonAnnotationIntrospector {
    public boolean hasIgnoreMarker(AnnotatedMember m) {
        boolean f = super.hasIgnoreMarker(m);
        if(!f) {
            ContextIgnored anno =  _findAnnotation(m, ContextIgnoreed.class);
            if(anno.value() == "验证值")
                return true;
        }
        return f;
    }
}

如上代码,即在原来只处理注解 JsonIgnore 的基础之上,再加上处理 注解 ContextIgnored.

但是在Jackson中,此上的代码只能被调用1次,即针对单个类,ObjectMapper会缓存每个class所对应的 JsonSerializer 实例。当上下文改变时,ObjectMapper 并不会重新再创建新的 JsonSerializer,而是直接使用已经缓存过的 对象来处理,这样实际上就达不到最初的需求。相应的缓存类,可参考类 DefaultSerializerProvider.findTypedValueSerializer 方法实现 ,以及其中的类 SerializerCache. 由于当前jackson的实现, SerializerCache 为1个final类,并不支持扩展,因此修改 DefaultSerializerProvider 中属性的方式不可处理。

不过可以通过类似Composite的方式来解决此问题,通过方法 ObjectMapper.setSerializerProvider 可以重新使用1个新的 DefaultSerializerProvider 对象,此此类是可以被继承的。当 ObjectMapper 在writeValue时,均会调用 DefaultSerializerProvider 中 serializeValue 方法来进行序列化操作。更准确的说,会先调用 createInstance 方法产生1个新的 DefaultSerializerProvider 来完成对象序列化操作。那么在此过程中,可以通过1个类似 Map 来组合不同上下文的处理。即在 createInstance 时,根据当前上下文的值委派给具体的类来进行处理。而当委派之后,再由后面的 DefaultSerializerProvider.Impl 类来完成最终 JsonSerializer 的创建和处理过程。我们构建的 Composite 对象即完成了 上下文 中转换的中间隔离作用,即根据 上下文值 的不同会有多个 DefaultSerializerProvider 存在,但具体对象的内部缓存的 JsonSerializer 即会根据上下文的不同有自己的处理逻辑。

示意代码如下:

class Composite {
    private Map<上下文, DefaultSerializerProvider> map;
    public Composite createInstance(SerializationConfig config, SerializerFactory jsf) {
        return map.computeIfAbsent(上下文, k-> new DefaultSerializerProvider.Impl());
    }
}

在具体实现中, 还需要考虑 DefaultSerializerProvider.copy 方法,这个在具体使用时具体实现即可.

利用 PropertyFilter 拦截属性输出

相比第1种手法的复杂及晦涩(可以理解为直接怼),这种手法就比较简单了。自 jackson 2.3 之后,提供接口 PropertyFilter ,以及对应的注解 JsonFilter 来允许对特定的类在处理字段时,进行额外的过滤处理操作。所谓的过滤操作,即将属性的实际操作转交由接口来完成。其接口下的1个参考实现 SimpleBeanPropertyFilter 即提供了 包含 和 排除的实现代码。我们只需要实现方法 boolean include(BeanPropertyWriter writer) 即可完成判断是否输出某个属性的操作。

此 PropertyFilter 的使用即在类上,通过 JsonFilter 定义此类的过滤器, 然后通过 ObjectMapper.setFilterProvider(SimpleFilterProvider) 添加 具体实现即可, 两都通过 value 来完成匹配过程。

PropertyFilter 接口是在每一次属性序列化均会处理,即意味着,如果方法 include(BeanPropertyWriter writer) 的返回值是变化的,即完成了动态拦截属性的作用。参考实现如下:

public class ContextIgnoredFilter extends SimpleBeanPropertyFilter {
    @Override
    protected boolean include(BeanPropertyWriter writer) {
        ContextIgnored anno = writer.getAnnotation(ContextIgnored.class);
        //上下文值与注解值判断处理...

        return true;
    }
}

这种实现方法,不需要添加额外的 AnnotationIntrospector, 也不需要调整 ObjectMapper 的 DefaultSerializerProvider 属性,并且在代码Review上更简单易懂。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Reality Is Broken

Reality Is Broken

Jane McGonigal / Penguin Press HC, The / 2011-1-20 / USD 26.95

Visionary game designer Jane McGonigal reveals how we can harness the power of games to solve real-world problems and boost global happiness. More than 174 million Americans are gamers, and......一起来看看 《Reality Is Broken》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具