Jackson自定义动态忽略http响应字段

概述

一般业务中一个接口可能会有不同用户返回不同字段的需求;

一般方案用@JsonInclude(JsonInclude.Include.NON_NULL)然后代码控制对应的属性值set null

但是这样复用性不高,springboot中使用的默认序列化包是jackson,那么自然就想到了可以拦截jackson的序列化,对部分类做一个序列化属性过滤.

我的jackson版本:2.13.3
springboot版本:2.7.4

逻辑概述:
通过com.fasterxml.jackson.annotation.JsonFilter注解,自定义过滤器
jackson序列化的时候如果使用了该过滤器
则进入过滤判定

该方案会替换jacksonobjectMapper中的FilterProviderdebug了下源码,默认是空的

方案二
通过jackson提供的自定义序列化,手动控制属性的序列化;
相对于方案一在细节上会繁琐一些,但是没有替换jacksonobjectMapper的默认配置

demo

//自定义注解
package com.xxx.common.xxx.annotation;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExceptFilter {
    //别名
    String aliasName() default "";

    //过滤策略
    String strategy() default "";
}


//自定义是否序列化接口,实际实现大家根据自己业务需求实现
package com.xxx.common.xxx.strategy;

import com.fasterxml.jackson.databind.JavaType;

public interface CustomExceptFilterStrategy {

    //序列化是否过滤;true:序列化;false:不序列化
    boolean filter(String fieldName, JavaType type);

}




//通过jackson提供的配置扩展入口,注册上过滤器
package com.xxx.xxx.xxx.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.xxx.xxx.xxx.annotation.CustomExceptFilter;
import com.xxx.xxx.xxx.strategy.CustomExceptFilterStrategy;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.util.Map;


@Configuration
@AutoConfigureBefore(Jackson2ObjectMapperBuilder.class)
@ConditionalOnClass(Jackson2ObjectMapperBuilderCustomizer.class)
public class JacksonSerPropertyFilterConfig implements InitializingBean {

    //序列化策略实现缓存map
    private static Map<String, CustomExceptFilterStrategy> customExceptFilterStrategyMap;


    @Bean
    public Jackson2ObjectMapperBuilderCustomizer customExceptFilterJackson2ObjectMapperBuilderCustomizer() {
        return jacksonObjectMapperBuilder -> {
            SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();
            FilterProvider filters = simpleFilterProvider
                    .addFilter("customBusinessExceptFilter", new CustomBusinessExceptFilter());

            jacksonObjectMapperBuilder.filters(filters);
        };
    }


    @Override
    public void afterPropertiesSet() {
        //缓存策略
        customExceptFilterStrategyMap = SpringUtil.getBeansOfType(CustomExceptFilterStrategy.class);
    }


    public static class CustomBusinessExceptFilter extends SimpleBeanPropertyFilter {

        @Override
        protected boolean include(BeanPropertyWriter writer) {
            return filter(writer.getName(), writer.getType(), writer.getAnnotation(CustomExceptFilter.class));
        }

        @Override
        protected boolean include(PropertyWriter writer) {
            return filter(writer.getName(), writer.getType(), writer.getAnnotation(CustomExceptFilter.class));
        }

        /**
         * 是否序列化
         */
        private boolean filter(String fieldName, JavaType type, CustomExceptFilter annotation){

            if(null == customExceptFilterStrategyMap || null == annotation) return true;
			//这是个自定义接口,返回true/false的,true代表需要序列化
			//注解中的strategy和CustomExceptFilterStrategy接口的实现类bean name对应上就行
            CustomExceptFilterStrategy customExceptFilterStrategy = customExceptFilterStrategyMap.get(annotation.strategy());

            if(null == customExceptFilterStrategy) return true;

            String aliasName = annotation.aliasName();
            if(StrUtil.isNotBlank(aliasName)){
                fieldName = aliasName;
            }

            return customExceptFilterStrategy.filter(fieldName, type);
        }
    }

}







//使用过滤器
package com.xxx.xxx.vo;

import com.xxx.xxx.xxx.annotation.CustomExceptFilter;
import com.xxx.xxx.xxx.constant.BusinessProFilterStrategyConstant;
import com.fasterxml.jackson.annotation.JsonFilter;
import lombok.Data;

//声明上自己的过滤器
@JsonFilter("customBusinessExceptFilter")
@Data
public class TaskVO {

	//声明该字段具体使用哪种策略过滤
    @CustomExceptFilter(strategy = BusinessProFilterStrategyConstant.xxx)
    private xxx xxxInfo

}



方案二

其实就是利用@JsonSerialize注解,对指定属性自定义序列化;
缺点:每个属性都要主动调用对应的序列化方法;

@Data
@JsonSerialize(using = CustomerSerial.class)
public class TestVO {

    private xxx testInfo;
    
}



package com.xxx.xxx.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * spring-web默认的序列化是jackson
 * 相关的序列化器bean也是托管在spring容器中--单例
 * org.springframework.http.converter.json.SpringHandlerInstantiator#serializerInstance(com.fasterxml.jackson.databind.SerializationConfig, com.fasterxml.jackson.databind.introspect.Annotated, java.lang.Class)
*/

@Slf4j
@Component
public class CustomerSerial extends StdSerializer<TestVO> {

    @Resource
    private RemoteUserService remoteUserService;

    public CustomerSerial() {
        super(TestVO.class);
    }

    @Override
    public void serialize(TestVO value, JsonGenerator gen, SerializerProvider provider) throws IOException {
    	//开始大括号
        gen.writeStartObject();

        //动态权限判断
        testSerialAuthority(value, gen);

		//结束大括号
        gen.writeEndObject();
    }

    private void testSerialAuthority(TestVO value, JsonGenerator gen) throws IOException {

        //filter有权限查看的属性,将过滤后的属性转成map
        Map<String, Object> propertyMap = filterNotVisibleProperty(value.getTestInfo());
		
		//无权限查看,整个对象都不序列化
        if(MapUtils.isEmpty(propertyMap)) return;

		//序列化,对象中部分有权限查看的属性字段
        gen.writeObjectField("testInfo", propertyMap);
    }

}


  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中,可以通过自定义Jackson来实现自定义序列化和反序列化的需求。以下是一个简单的示例,演示了如何自定义Jackson。 首先,我们需要创建一个Jackson的ObjectMapper Bean,并指定我们自定义的序列化器和反序列化器。示例中,我们自定义了一个格式化日期的序列化器和反序列化器。 ```java @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(Date.class, new CustomDateSerializer()); module.addDeserializer(Date.class, new CustomDateDeserializer()); objectMapper.registerModule(module); return objectMapper; } } ``` 然后,我们需要实现自定义的序列化器和反序列化器。示例中,我们自定义了一个格式化日期的序列化器和反序列化器。 ```java public class CustomDateSerializer extends JsonSerializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(dateFormat.format(value)); } } public class CustomDateDeserializer extends JsonDeserializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String dateValue = p.getText(); try { return dateFormat.parse(dateValue); } catch (ParseException e) { throw new RuntimeException(e); } } } ``` 现在,我们已经完成了自定义Jackson的配置。在使用时,我们可以直接使用@Autowired注解注入ObjectMapper Bean,或者使用@JsonComponent注解来标识我们的自定义序列化器和反序列化器。 ```java @RestController public class UserController { @Autowired private ObjectMapper objectMapper; @PostMapping("/user") public User addUser(@RequestBody User user) throws JsonProcessingException { String json = objectMapper.writeValueAsString(user); System.out.println(json); return user; } } @JsonComponent public class DateJsonComponent { public static class Serializer extends JsonSerializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeString(dateFormat.format(value)); } } public static class Deserializer extends JsonDeserializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String dateValue = p.getText(); try { return dateFormat.parse(dateValue); } catch (ParseException e) { throw new RuntimeException(e); } } } } ``` 以上就是自定义Jackson的简单示例。通过自定义Jackson,我们可以轻松实现自定义的序列化和反序列化需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值