Jackson-自定义注解及实现数据脱敏、枚举转换

Hi,大家好,我是抢老婆酸奶的小肥仔。

上一章,我们介绍了下Jackson及相关的注解,其实我们可以通过仿照一些注解来实现自定义以满足我们自己的业务需求,这一章,我们来说说jackson提供的自定义注解及一些应用吧。

废话不多说,撸起来。

1、自定义注解

在Jackson中使用 @JacksonAnnotationsInside来标注当前定义的注解为jackson注解。我们通过一个简单例子来看下。

1.1 自定义注解

/*** @author: jiangjs* @description:* @date: 2023/6/15 10:07**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"id","nickName","password","name"})
public @interface DataOrderAndFilter {
}

上述注解实现了序列化时排列的顺序,以及不显示数值为null的字段。

1.2 创建实体

/*** @author: jiangjs* @description:* @date: 2023/6/15 10:11**/
@Data
@AllArgsConstructor
@DataOrderAndFilter
public class CustomDataEnter {private Integer id;private String name;private String nickName;private String password;
}

1.3 测试

@Test
public void useJacksonAnnotationsInside() throws JsonProcessingException {CustomDataEnter dataEnter = new CustomDataEnter(1, "zhangsan", null, "123456");String json = new ObjectMapper().writeValueAsString(dataEnter);log.info("使用@JacksonAnnotationsInside实现自定义注解序列化json:" + json);
}

输出:

上述代码通过简单的例子说明了@JacksonAnnotationsInside可以自定义注解。下面我们来看看在我们日常工作中在哪些地方可以使用。

2、自定义注解应用

2.1 实现数据脱敏

结合@JsonSerialize在数据进行序列化时,进行数据的脱敏。

需求:用户信息在返回到前端时,对用户名,身份证号,手机号等进行数据脱敏。

  1. 用户名:如果两个字时,只保留姓,后面为*,例如:张三,脱敏后:张*,超过三个字时,则保留前后两个字,中间使用*,例如:张小四,脱敏后:张*四
  2. 身份证号:前后各保留四位,中间用四个连接,例如:450218199103458901,脱敏后:4501***8901
  3. 手机号:保留前三位和后四位,中间四位用连接,例如:13456789012,脱敏后:134***9012

根据上述需求,我们先建一个枚举,实现不同类型数据脱敏,也方便后期实现其他字段脱敏时,只要在这个枚举类中加入就行。

2.1.1 创建脱敏枚举
/*** @author: jiangjs* @description: 脱敏策略* @date: 2022/4/15 16:23**/
public enum DesensitizedStrategy {/*** 用户信息脱敏*/USER_NAME(s -> s.replaceAll("(\S)\S(\S*)", "$1*$2")),/*** 身份证信息脱敏*/ID_CARD(s -> s.replaceAll("(\d{4})\d{10}(\w{4})", "$1****$2")),/*** 手机号脱敏*/PHONE(s -> s.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2"));private final Desensitized desensitized;DesensitizedStrategy(Desensitized desensitized){this.desensitized = desensitized;}public Desensitized getDesensitize() {return desensitized;}
}

定义的脱敏字段中采用了Function函数来实现。

/*** @author: jiangjs* @description: 脱敏接口* @date: 2022/4/15 16:22**/
public interface Desensitized extends Function<String,String> {
}
2.1.2 创建注解
/*** @author: jiangjs* @description: 使用jackson进行数据脱敏* @date: 2023/6/1 15:50**/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = JacksonDataDesensitized.class)
public @interface JsonSensitive {DesensitizedStrategy strategy();
}

@JsonSerialize:实现数据的序列化,using则是实现序列化的具体类

2.1.3 创建序列化类
/*** @author: jiangjs* @description: 通过jackson数据序列化来实现脱敏* @date: 2023/6/1 15:54**/
@NoArgsConstructor
@AllArgsConstructor
public class JacksonDataDesensitized extends JsonSerializer<String> implements ContextualSerializer {/*** 脱敏数据*/private DesensitizedStrategy strategy;@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {gen.writeString(strategy.getDesensitize().apply(value));}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {if (Objects.isNull(property)){return prov.findNullValueSerializer(null);}//校验当前bean是否为Stringif (Objects.equals(property.getType().getRawClass(),String.class)){JsonSensitive sensitive = property.getAnnotation(JsonSensitive.class);if (Objects.isNull(sensitive)){sensitive = property.getContextAnnotation(JsonSensitive.class);}if (Objects.nonNull(sensitive)){return new JacksonDataDesensitized(sensitive.strategy());}}return prov.findValueSerializer(property.getType(), property);}
}

创建的序列化类继承了JsonSerializer类,指定了数据的类型,实现了ContextualSerializer上下文序列化来保证JsonSerializer不为空。

JsonGenerator:用来生成Json格式的内容,可以使用JsonFactory来生成一个实例。

2.1.4 使用注解
/*** @author: jiangjs* @description:* @date: 2022/4/15 11:33**/
@Data
@Accessors(chain = true)
@EqualsAndHashCode
public class UserInfo implements Serializable{/*** 主键*/private Integer id;/*** 用户名*/private String userName;/*** 昵称*/@JsonSensitive(strategy = DesensitizedStrategy.USER_NAME)private String nickName;/*** 手机号*/@JsonSensitive(strategy = DesensitizedStrategy.PHONE)private String phone;/*** 身份证号*/@JsonSensitive(strategy = DesensitizedStrategy.ID_CARD)private String idCard;/*** 创建时间*/@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss",timezone = "GTM+8")private Date createTime;private static final long serialVersionUID = 1L;
}

在实体中通过注解来指定各字段脱敏的类型。

2.1.4 测试
/*** @author: jiangjs* @description:* @date: 2023/6/15 11:31**/
@RestController
@RequestMapping("/desensitized")
public class DesensitizedController {@GetMapping("/getUserInfo.do")public UserInfo getUserInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(1).setUserName("zhangsan").setNickName("张三").setIdCard("450218199103458901").setPhone("13456789012");return userInfo;}
}

输出:

从输出结果中,我们可以看到使用注解的字段已经实现数据脱敏。

2.2 通用枚举转换

需求:在实体中有时候会用到枚举,前端往往会传递一个值保存到数据,但是查询返回时往往又是对应的另外的值。因此做一个通用的枚举类型转换,查询数据时进行赋值。

2.2.1 创建注解
/*** @author: jiangjs* @description:* @date: 2023/6/15 16:08**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = TypeChangeSerialize.class)
public @interface TypeChange {//值得替换 导出是{a_id,b_id} 导入反过来,所以只用写一个String[] replace() default {};
}

replace():仿照Easypoi中@Excel注解中的replace。

2.2.1 创建序列化实现类
/*** @author: jiangjs* @description:* @date: 2023/6/15 16:21**/
@AllArgsConstructor
public class TypeChangeSerialize extends JsonSerializer<Object> implements ContextualSerializer {private final String[] annotationValues;@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {String[] types = this.annotationValues;Map<String, String> typeMap = new ConcurrentHashMap<>();if (Objects.nonNull(types) && types.length > 0) {for (String type : types) {String[] s = type.split("_");typeMap.put(s[0],s[1]);}}gen.writeString(typeMap.get(String.valueOf(value)));}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException {if (Objects.isNull(property)){return provider.findNullValueSerializer(null);}//校验当前bean是否为String或Integerif (Objects.equals(property.getType().getRawClass(),String.class) ||Objects.equals(property.getType().getRawClass(),Integer.class)){TypeChange typeChange = property.getAnnotation(TypeChange.class);if (Objects.isNull(typeChange)){typeChange = property.getContextAnnotation(TypeChange.class);}if (Objects.nonNull(typeChange)){return new TypeChangeSerialize(typeChange.replace());}}return provider.findValueSerializer(property.getType(), property);}
}

annotationValues:定义接受注解replace的值。

new TypeChangeSerialize(typeChange.replace()):初始化序列化处理类,将获取到的replace()值赋值给annotationValues。

2.2.3 使用注解
/*** @author: jiangjs* @description:* @date: 2022/4/15 11:33**/
@Data
@Accessors(chain = true)
@EqualsAndHashCode
public class UserInfo implements Serializable{/*** 主键*/private Integer id;@TypeChange(replace = {"1_男","2_女"})private Integer gender;private static final long serialVersionUID = 1L;
}

@TypeChange(replace = {“1_男”,“2_女”}):按照注解的规则,赋值replace。

2.2.4 测试
/*** @author: jiangjs* @description:* @date: 2023/6/15 11:31**/
@RestController
@RequestMapping("/desensitized")
public class DesensitizedController {@GetMapping("/getUserInfo.do")public UserInfo getUserInfo(){UserInfo userInfo = new UserInfo();userInfo.setId(1).setGender(1);return userInfo;}
}

输出:

从输出结果来看,gender赋值为1,序列化后返回的是对应的中文:男。也就是说,这个实现了我们需求,即字段进行了替换。

上述就是我在工作中使用jackson的一些应用,大家可以举一反三来实现自己的需求,例如:自定义时间格式化等等。

源码:https://github.com/lovejiashn/use_jackson.git

工欲善其事,必先利其器,小伙伴们撸起来吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/5166.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Virtualbox7.0.10--创建虚拟机

前言 下载Virtualbox7.0.10&#xff0c;可参考《Virtualbox–下载指定版本》 Virtualbox7.0.10具体安装步骤&#xff0c;可参考《Virtualbox7.0.10的安装步骤》 Virtualbox7.0.10创建虚拟机&#xff0c;可参考《Virtualbox7.0.10–创建虚拟机》 Virtualbox7.0.10安装Ubuntu20.0…

后端如何处理接口的重复调用

首先是&#xff0c;原理在请求接口之前&#xff0c;使用过滤器拦截数据&#xff0c;来进行判断两次数据是否一致。 1.自定义注解 2.创建一个Handler处理器 3.RepeatSubmitInterceptor的实现类 4.过滤器的配置

大型企业总分支多区域数据传输,效率为先还是安全为先?

大型企业为了业务拓展需要&#xff0c;会在全国乃至全球各地设立分公司和办事机构&#xff0c;以便更好地处理当地事务&#xff0c;并进行市场的开拓和客户维护&#xff0c;此时&#xff0c;企业内部就衍生出了新的业务需求&#xff0c;即多区域数据传输。 多区域很难准确定义&…

[C++][算法基础]最大不相交区间数量(贪心 + 区间问题2)

给定 &#x1d441; 个闭区间 [&#x1d44e;&#x1d456;,&#x1d44f;&#x1d456;]&#xff0c;请你在数轴上选择若干区间&#xff0c;使得选中的区间之间互不相交&#xff08;包括端点&#xff09;。 输出可选取区间的最大数量。 输入格式 第一行包含整数 &#x1d4…

Aiseesoft Data Recovery for Mac:专业数据恢复软件

Aiseesoft Data Recovery for Mac是一款高效且专业的数据恢复软件&#xff0c;专为Mac用户量身打造。 Aiseesoft Data Recovery for Mac v1.8.22激活版下载 无论是由于误删、格式化还是系统崩溃等原因导致的数据丢失&#xff0c;Aiseesoft都能帮助您快速找回。 它采用先进的扫描…

IDEA插件-通义灵码 VS ChatGPT-EasyCode

智能编码助手新时代&#xff1a;通义灵码 vs ChatGPT-EasyCode 随着人工智能技术的飞速发展&#xff0c;智能编码助手逐渐成为程序员的必备工具。它们可以帮助程序员提高编码效率&#xff0c;降低代码缺陷率&#xff0c;并解放创造力。 目前市场上涌现出了众多智能编码助手&a…

Golang | Leetcode Golang题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; func getPermutation(n int, k int) string {factorial : make([]int, n)factorial[0] 1for i : 1; i < n; i {factorial[i] factorial[i - 1] * i}k--ans : ""valid : make([]int, n 1)for i : 0; i < len(valid); i {…

【哈希】Leetcode 217. 存在重复元素

题目讲解 217. 存在重复元素 算法讲解 使用set集合完成元素的存储&#xff0c;当我们将当前元素插入到集合单中&#xff0c;如果insert的返回值的pair.second等于false说明当前元素已经存在&#xff0c;反之元素在集合中存在 class Solution { public:bool containsDuplica…

开放式耳机哪个牌子好?五大爆款机型大盘点

开放式耳机采用挂耳设计&#xff0c;体积小巧&#xff0c;携带方便&#xff0c;并且更加通风透气&#xff0c;避免了耳朵过热和出汗导致的问题&#xff1b;更轻的重量能有效减少长期佩戴对耳朵带来的压力&#xff0c;佩戴时舒适度直接爆表&#xff0c;在跑步、爬山、打球等户外…

ip ssl证书无限端口

IP SSL证书是由CA认证机构颁发的一种特殊数字证书。大部分SSL数字证书都需要用户使用域名进行申请&#xff0c;想要对公网IP地址加密实现https访问就需要申请IP SSL证书。IP SSL证书采用了强大的加密算法&#xff0c;可以有效地防止数据在传输过程中被窃取或篡改&#xff0c;具…

Copilot Workspace是GitHub对人工智能驱动的软件工程的诠释

软件开发的未来是人工智能驱动的集成开发环境吗&#xff1f;至少GitHub 是这样想的。 在今年初秋于旧金山举行的 GitHub Universe 年度大会之前&#xff0c;GitHub 发布了 Copilot Workspace&#xff0c;这是一种开发环境&#xff0c;利用 GitHub 所称的 “Copilot 驱动的代理…

Pytest自动化测试框架详解

今日之失&#xff0c;未必不为后日之得。大家好&#xff0c;刚才在翻看之前整理的一些关于自动化测试的文档&#xff0c;突然发现一个比较详细的关于pytest框架的介绍和使用文章&#xff0c;对于打算使用python进行自动化测试编写的小伙伴&#xff0c;还是很值得一看的&#xf…

深度学习之基于YOLOv5烟花燃放智能检测系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着科技的进步和人们对环保、安全意识的提高&#xff0c;传统的烟花燃放监管方式已逐渐不能满足现代…

RSA加密---java和node兼容版(可直接复制使用)

目录 背景 实现 一、node代码 1、引入依赖 2、生成公钥和私钥 3、生成工具类 二、java代码 背景 本来项目的后端是node&#xff0c;里面登录接口用的是后端生成RSA公钥和私钥&#xff0c;公钥给前端网页用来加密&#xff0c;node后端解密&#xff0c;一切很和谐&#x…

什么是MOM?为什么它是趋势

制造运营管理&#xff08;MOM&#xff09; 制造运营管理&#xff08;MOM&#xff09;旨在优化制造流程的效率和有效性。它涵盖制造执行、质量管理、生产计划和调度以及制造智能等功能。这种解决方案以全面的方式管理和增强制造流程。 MOM的功能特点 对于MOM的功能特点来说&…

Office Word自动编号转文本

原理 使用office自带的宏功能&#xff0c;一键替换 过程 调出word的“开发工具”选项 文件->选项->自定义功能区->选中开发工具->确定 创建宏 开发工具->宏->创建宏 编写宏 在弹出来的框里&#xff0c;替换代码为 Sub num2txt() ActiveDocument.…

前端请求没问题,后端正常运行,但查不出数据

写代码时写得快了些&#xff0c;Orders.的订单状态写错了CONFIRMED 改成COMPLETED

selenium 4.x 入门(环境搭建、八大元素定位)

背景 Web自动化测现状 1. 属于 E2E 测试 2. 过去通过点点点 3. 好的测试&#xff0c;还需要记录、调试网页的细节 一、selenium4.x环境搭建 一键搭建 pip3 install webdriver-helper 安装后自动的完成&#xff1a; 1. 查看浏览器的版本号 2. 查询操作系统的类型…

C++栈和队列模拟

栈和队列所用的容器默认都为deque&#xff0c;这种容器可以看作是一种vector和list的中间性能容器。 而deque虽然头插、尾插效率很好&#xff0c;且支持 [ ] 访问&#xff08;默认容器为它的原因&#xff09;&#xff0c;但是 他的缺点也很明显&#xff1a; 1.中间插入删除会…

Git中单独的功能特性分支是什么含义

在Git中&#xff0c;一个"功能特性分支"&#xff08;通常简称为“特性分支”&#xff09;是指从主开发分支&#xff08;比如main或master&#xff09;独立出来的分支&#xff0c;专门用于开发一个新功能、修复一个bug&#xff0c;或者进行实验性的尝试。使用特性分支…