• 通过Jackson实现敏感词过滤和类型转换的功能


    背景

    目前正在做老项目迁移重构工作(Clojure转Java),因历史原因项目中给到端上的Json数据中和(id/user_id/order_id)等相关的id字段需要以string类型给到端上。

    已有clojure实现

    祖传项目是通过clojure实现的,clojure是一门弱语言和JavaScript一样任何对象都是看做一个map,所以在clojure里面针对这个long->string的功能,实现起来很简单;只需要递归遍历map判断key是否存在于定义的set里面即可,如果满足条件,直接根据是否为list进行toString的处理;

    需要实现的功能

    实现一个Java方法,传入一个对象进行返回序列化的Json字符串。序列化的同时如果遇到满足条件的字段需要对该字段进行 toString 处理,如果是List<Objec>需要转换为 List<String>

    如何通过Java实现?

    因为项目中使用的是jackson序列化json,所以很容易想到的就是使用jackson提供的AnnotationIntrospector,在对应的字段上添加 注解 ,然后自定义AnnotationIntrospector在匹配对应注解的时候返回自定义的Serializer。

    tips:仅使用@JsonSerialize(using = ToStringSerializer.class)是不能处理 List<Long> -> List<String>这种情况的

    AnnotationIntrospector 实现 long->toString 功能

     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.FIELD})
     public @interface CustomToString {
     
     }
     
     @Data
     public class XxxDTO{
       
         @CustomToString
         private  Long id;
     
         @CustomToString
         private  List<Long>  uids;
       
         private String desc;
       
     }
     
     class IdFieldToStringSerializer extends JsonSerializer<Object> {
     
             public static final IdFieldToStringSerializer INSTANCE = new IdFieldToStringSerializer();
     
             private IdFieldToStringSerializer() {
            }
     
             @Override
             public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                 if (value instanceof Collection) {
                     gen.writeStartArray();
                     for (Object item : (Collection) value) {
                         gen.writeString(String.valueOf(item));
                    }
                     gen.writeEndArray();
                     return;
                }
                 gen.writeString(String.valueOf(value));
            }
     
        }
     
     public class DefaultAnnotationIntrospector extends JacksonAnnotationIntrospector {
     
       @Override
         public Object findSerializer(Annotated a) {
             if(a.hasAnnotation(CustomToString.class)){
               return IdFieldToStringSerializer.INSTANCE;
            }
             return super.findSerializer(a);
        }
     
     }

    扩展-AnnotationIntrospector 实现 字段过滤 功能(也可以注解增加属性,用于指定环境过滤等功能)

     @Retention(RetentionPolicy.RUNTIME)
     @Target({ElementType.FIELD})
     public @interface CustomFilter {
     
     }
     
     @Data
     public class XxxDTO{
       
         @CustomFilter
         private  Long id;
       
         private String desc;
       
     }
     
     public class DefaultAnnotationIntrospector extends JacksonAnnotationIntrospector {
     
           @Override
           public boolean hasIgnoreMarker(AnnotatedMember m) {
               if(m.hasAnnotation(CustomFilter.class)){
                 return true;
              }
               return super.hasIgnoreMarker(m);
            }
     
     }

    这里字段过滤其实功能只相当于 @JsonIgnore ,需要增加CustomToString的属性,比如 env;然后在DefaultAnnotationIntrospector中根据env值再做判断

    AnnotationIntrospector实现分析

    副作用

    在后续开发过程中,在我们定义新的Object时,需要参照对应的field表对对应字段增加注解。如果此时突然要对某个字段过滤或者toString,那又得从头挨着挨着检查添加注解。增加开发成本和维护成本

    优化升级

    通过对 new ObjectMapper().writeValueAsString() 的执行流程 debug分析,发现对象的属性都被解析成 BeanProperty了,然后会调用 SerializerProvider 的方法寻找合适的Serializer进行序列化

    实现 long->toString 功能
     import com.fasterxml.jackson.core.JsonGenerator;
     import com.fasterxml.jackson.databind.BeanProperty;
     import com.fasterxml.jackson.databind.JavaType;
     import com.fasterxml.jackson.databind.JsonMappingException;
     import com.fasterxml.jackson.databind.JsonSerializer;
     import com.fasterxml.jackson.databind.SerializationConfig;
     import com.fasterxml.jackson.databind.SerializerProvider;
     import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
     import com.fasterxml.jackson.databind.ser.SerializerFactory;
     import java.io.IOException;
     import java.util.Arrays;
     import java.util.Collection;
     import java.util.HashSet;
     import java.util.Optional;
     import java.util.Set;
     import java.util.stream.Collectors;
     import lombok.extern.slf4j.Slf4j;
     import org.springframework.util.ObjectUtils;
     
     /**
      * @class-name: IdFieldToStringSerializerProvider
      * @description:
      * @author: Mr.Zeng
      */
     @Slf4j
     public class IdFieldToStringSerializerProvider extends DefaultSerializerProvider {
     
         private static final String FILED_SOURCE = "admin_id、id、msg_id、uid、uids、user_id、member_uids、parent_id";
     
         private static final Set<String> FILED_SET = new HashSet<>(
                 Arrays.stream(FILED_SOURCE.split(";|,|;|,|、"))
                        .map(String::trim)
                        .filter(o -> !ObjectUtils.isEmpty(o))
                        .collect(Collectors.toSet())
        );
     
         public IdFieldToStringSerializerProvider() {
             super();
        }
     
         public IdFieldToStringSerializerProvider(IdFieldToStringSerializerProvider src) {
             super(src);
        }
     
         protected IdFieldToStringSerializerProvider(SerializerProvider src, SerializationConfig conf, SerializerFactory f) {
             super(src, conf, f);
        }
     
         @Override
         public DefaultSerializerProvider copy() {
             if (this.getClass() != IdFieldToStringSerializerProvider.class) {
                 return super.copy();
            }
             return new IdFieldToStringSerializerProvider(this);
        }
     
         @Override
         public IdFieldToStringSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
             return new IdFieldToStringSerializerProvider(this, config, jsf);
        }
     
         @Override
         public void serializeValue(JsonGenerator gen, Object value) throws IOException {
             //log.debug("serializeValue[JsonGenerator,Object], {}", value);
             super.serializeValue(gen, value);
        }
     
         @Override
         public JsonSerializer<Object> findValueSerializer(Class<?> valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findValueSerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findValueSerializer(JavaType valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findValueSerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findPrimaryPropertySerializer(JavaType valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findPrimaryPropertySerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findPrimaryPropertySerializer(Class<?> valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findPrimaryPropertySerializer(valueType, property);
        }
     
         private JsonSerializer<Object> findSerializerByBeanProperty(BeanProperty property) {
             String propertyName = Optional.ofNullable(property).map(BeanProperty::getName).orElse("");
             //log.debug("findValueSerializer[Class<?>:{},property:{}]", valueType.getName(), propertyName);
             if (FILED_SET.contains(propertyName)) {
                 if (log.isDebugEnabled()) {
                     log.debug("property[{}] meets the “ToString“ condition. using IdFieldToStringSerializer", propertyName);
                }
                 return IdFieldToStringSerializer.INSTANCE;
            }
             return null;
        }
     
         static class IdFieldToStringSerializer extends JsonSerializer<Object> { 
    ​ 
           public static final IdFieldToStringSerializer INSTANCE = new IdFieldToStringSerializer(); 
    ​ 
           private IdFieldToStringSerializer() { 
          } 
    ​ 
           @Override 
           public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 
               if (value instanceof Collection) { 
                   gen.writeStartArray(); 
                   for (Object item : (Collection) value) { 
                       gen.writeString(String.valueOf(item)); 
                  } 
                   gen.writeEndArray(); 
                   return; 
              } 
               gen.writeString(String.valueOf(value)); 
          } 
    ​ 
      } 
    ​ 
    }
    实现 字段过滤 功能 (这种方式仅仅是伪实现,只能实现值转为 * 或者 空字符串,不支持字段过滤)
     import com.fasterxml.jackson.core.JsonGenerator;
     import com.fasterxml.jackson.databind.BeanProperty;
     import com.fasterxml.jackson.databind.JavaType;
     import com.fasterxml.jackson.databind.JsonMappingException;
     import com.fasterxml.jackson.databind.JsonSerializer;
     import com.fasterxml.jackson.databind.SerializationConfig;
     import com.fasterxml.jackson.databind.SerializerProvider;
     import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
     import com.fasterxml.jackson.databind.ser.SerializerFactory;
     import java.io.IOException;
     import java.util.Arrays;
     import java.util.HashSet;
     import java.util.Optional;
     import java.util.Set;
     import java.util.stream.Collectors;
     import lombok.extern.slf4j.Slf4j;
     import org.springframework.util.ObjectUtils;
     /**
      * @class-name: MaskFiledSerializerProvider
      * @description:
      * @author: Mr.Zeng
      */
     @Slf4j
     public class MaskFiledSerializerProvider extends DefaultSerializerProvider {
     
         private static final String FILED = "ticket,text,content,oauth,tcp,ws_host,file";
     
         private static final Set<String> FILED_SET = new HashSet<>(
                 Arrays.stream(FILED.split(";|,|;|,|、"))
                        .map(String::trim)
                        .filter(o -> !ObjectUtils.isEmpty(o))
                        .collect(Collectors.toSet())
        );
     
         public MaskFiledSerializerProvider() {
             super();
        }
     
         public MaskFiledSerializerProvider(MaskFiledSerializerProvider src) {
             super(src);
        }
     
         protected MaskFiledSerializerProvider(SerializerProvider src, SerializationConfig config, SerializerFactory f) {
             super(src, config, f);
        }
     
         @Override
         public DefaultSerializerProvider copy() {
             if (this.getClass() != MaskFiledSerializerProvider.class) {
                 return super.copy();
            }
             return new MaskFiledSerializerProvider(this);
        }
     
         @Override
         public MaskFiledSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
             return new MaskFiledSerializerProvider(this, config, jsf);
        }
     
         @Override
         public void serializeValue(JsonGenerator gen, Object value) throws IOException {
             //log.debug("serializeValue[JsonGenerator,Object], {}", value);
             super.serializeValue(gen, value);
        }
     
         @Override
         public JsonSerializer<Object> findValueSerializer(Class<?> valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findValueSerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findValueSerializer(JavaType valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findValueSerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findPrimaryPropertySerializer(JavaType valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findPrimaryPropertySerializer(valueType, property);
        }
     
         @Override
         public JsonSerializer<Object> findPrimaryPropertySerializer(Class<?> valueType, BeanProperty property)
                 throws JsonMappingException {
             JsonSerializer<Object> target = findSerializerByBeanProperty(property);
             if (target != null) {
                 return target;
            }
             return super.findPrimaryPropertySerializer(valueType, property);
        }
     
         private JsonSerializer<Object> findSerializerByBeanProperty(BeanProperty property) {
             String propertyName = Optional.ofNullable(property).map(BeanProperty::getName).orElse("");
             //log.debug("findValueSerializer[Class<?>:{},property:{}]", valueType.getName(), propertyName);
             if (FILED_SET.contains(propertyName)) {
                 if (log.isDebugEnabled()) {
                     log.debug("property[{}] meets the “Mask“ condition. value to '***' ", propertyName);
                }
                 return MaskSerializer.INSTANCE;
            }
             return null;
        }
     
         static class MaskSerializer extends JsonSerializer<Object> { 
    ​ 
           public static final MaskSerializer INSTANCE = new MaskSerializer(); 
    ​ 
           private MaskSerializer() { 
          } 
    ​ 
           @Override 
           public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException { 
               gen.writeString("***"); 
          } 
    ​ 
      } 
    ​ 
    }
     
  • 相关阅读:
    新巴巴运动网 项目第二天
    新巴巴运动网 项目第一天
    pandas.read_csv()参数(转载)
    from sklearn.datasets import make_classification创建分类数据集
    【剑指offer】08二叉树的下一个节点,C++实现
    【剑指offer】滑动窗口的最大值,C++实现
    【剑指offer】找出数组中任意重复的数字(不修改数组),C++实现
    步入element-ui踩坑记
    步入vue.js踩坑记
    nvm安装与使用
  • 原文地址:https://www.cnblogs.com/yuanzessrs/p/16166864.html
Copyright © 2020-2023  润新知