基于注解实现jackson动态JsonProperty
基于注解实现jackson动态JsonProperty
@JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,但是值是固定的,(不贴代码,可以看其他博客)
目前跟某公司做接口对接时数据格式是这样的:
接口A: 接口B:

映射实体对象 :(这里用到lombok工具)



报文第三个字段与具体的pojo类有关,@JsonProperty在这里显然无法实现;
这里写两种序列化方式:
第一种 : 注解@JsonAnyGetter (自己了解下这个注解,这里变相使用了下)
映射实体对象类做下调整

测试代码(注意测试时每个类里加些属性,否则抛异常):

第二种 : 自定义注解来实现(我不想做pojo类的变动,感觉Map怪怪的,因为这里接口对接这里是一个对象)
先贴测试代码:


这里@DynamicJsonProperty为自定义的注解,(不了解自定义注解的可以先百度下"jackson自定义序列化注解实现自己的序列逻辑")
下面是该注解相应的代码:
1 import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
2 import com.fasterxml.jackson.core.JsonGenerator;
3 import com.fasterxml.jackson.core.json.UTF8JsonGenerator;
4 import com.fasterxml.jackson.core.json.WriterBasedJsonGenerator;
5 import com.fasterxml.jackson.databind.BeanProperty;
6 import com.fasterxml.jackson.databind.JsonMappingException;
7 import com.fasterxml.jackson.databind.JsonSerializer;
8 import com.fasterxml.jackson.databind.SerializerProvider;
9 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
10 import com.fasterxml.jackson.databind.ser.ContextualSerializer;
11 import com.sinosoft.ehs.utils.Reflector;
12 import com.sinosoft.ehs.utils.Strings;
13
14 import java.io.IOException;
15 import java.lang.annotation.*;
16 import java.util.function.Function;
17
18 @Documented
19 @Retention(RetentionPolicy.RUNTIME)
20 @Target({ElementType.FIELD,ElementType.METHOD})
21 @JacksonAnnotationsInside
22 @JsonSerialize(using = DynamicJsonProperty.DynamicJsonPropertySerializer.class)
23 public @interface DynamicJsonProperty {
24
25 AS[] value() default {};
26
27 Strategy strategy() default Strategy.CLASS_SIMPLE_NAME_LOWERCASE;
28
29
30 @interface AS{
31
32 String name();
33
34 Class<?> value();
35 }
36
37 @Retention(RetentionPolicy.RUNTIME)
38 @interface Name{
39 String value();
40 }
41
42 interface KeyName{
43 String jsonPropertyName();
44 }
45
46
47 enum Strategy{
48
49 CLASS_NAME(object -> object.getClass().getName()),
50 CLASS_SIMPLE_NAME(object -> object.getClass().getSimpleName()),
51 CLASS_SIMPLE_NAME_LOWERCASE(object -> object.getClass().getSimpleName().toLowerCase()),
52 CLASS_SIMPLE_NAME_UPPERCASE(object -> object.getClass().getSimpleName().toUpperCase());
53
54 private Function<Object,String> function;
55
56 Strategy(Function<Object,String> function){
57 this.function = function;
58 }
59
60 public String getName(Object object){
61 if(object==null)
62 return null;
63 return function.apply(object);
64 }
65
66 }
67
68 class DynamicJsonPropertySerializer extends JsonSerializer<Object> implements ContextualSerializer {
69 /**key值是否重置*/
70 private boolean gotName;
71 private DynamicJsonProperty annotation;
72 /**原先的key值*/
73 private String originalName;
74
75
76 @Override
77 public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
78 if(value != null && !gotName){
79 String name = getKeyName(value);
80 if(jsonGenerator instanceof WriterBasedJsonGenerator){
81 char[] _outputBuffer = Reflector.getFieldValue(jsonGenerator,"_outputBuffer",char[].class);
82 char _quoteChar = Reflector.getFieldValue(jsonGenerator,"_quoteChar",char.class);
83 int start = String.valueOf(_outputBuffer).lastIndexOf(originalName);
84 int end = start+name.length()+1;
85 Reflector.setFieldValue(jsonGenerator,"_outputTail",end);
86
87 for(int i=0;i<name.length();i++){
88 _outputBuffer[start+i] = name.charAt(i);
89 }
90 _outputBuffer[start+name.length()] = _quoteChar;
91 }
92
93 if(jsonGenerator instanceof UTF8JsonGenerator){
94 byte[] _outputBuffer = Reflector.getFieldValue(jsonGenerator,"_outputBuffer",byte[].class);
95 int _outputTail = Reflector.getFieldValue(jsonGenerator,"_outputTail",int.class);
96
97 byte _quoteChar = Reflector.getFieldValue(jsonGenerator,"_quoteChar",byte.class);
98 System.err.println(new String(_outputBuffer,"UTF-8"));
99 int startIndex = getStartIndex(_outputBuffer, _outputTail, 1)+1;
100 byte[] nameBytes = name.getBytes("UTF-8");
101 int end = startIndex+nameBytes.length+1;
102 Reflector.setFieldValue(jsonGenerator,"_outputTail",end);
103
104 for(int i=0;i<nameBytes.length;i++){
105 _outputBuffer[startIndex+i] = nameBytes[i];
106 }
107 _outputBuffer[startIndex+nameBytes.length] = _quoteChar;
108
109
110 }
111 }
112 jsonGenerator.writeObject(value);
113 }
114
115
116 @Override
117 public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
118 annotation = beanProperty.getAnnotation(DynamicJsonProperty.class);
119 /**
120 * PropertyName fullName = beanProperty.getFullName();
121 * PropertyName._simpleName 字段即为key健的值,反射直接赋值即可实现,
122 * 不过这里未获取操作的Object对象,只能获取Field或者Method
123 * createContextual优先执行,serialize之后执行,这里顺序问题也不能操作
124 * 之后在细看程序研究,或者有大牛能解决的联系学习下
125 */
126 originalName = beanProperty.getName();
127 return this;
128 }
129
130 private String getKeyName(Object value){
131 Class<?> valueType = value.getClass();
132 AS[] asAnnotations = annotation.value();
133 for(int i=0;i<asAnnotations.length;i++){
134 if(asAnnotations[i].value() == valueType)
135 return asAnnotations[i].name();
136 }
137
138 Annotation[] annotations = valueType.getAnnotations();
139
140 Name nameAnnotation = valueType.getAnnotation(DynamicJsonProperty.Name.class);
141 if(nameAnnotation!=null)
142 return nameAnnotation.value();
143
144 if(value instanceof DynamicJsonProperty.KeyName){
145 String name = ((DynamicJsonProperty.KeyName)value).jsonPropertyName();
146 if(!Strings.isBlank(name))
147 return name;
148 }
149
150 return annotation.strategy().getName(value);
151 }
152
153 private int getStartIndex(byte[] _outputBuffer,int _outputTail,int index){
154 int currentIndex = 0;
155 for(int i=_outputTail;i>=0;i--){
156 if(_outputBuffer[i] == 34){
157 if(currentIndex == index)
158 return i;
159 currentIndex++;
160 }
161 }
162 return -1;
163 }
164
165 }
166
167
168 }
四种写法实例:





注意 : 以上四种方法字段data均需加注解@DynamicJsonProperty
反序列话直接加set方法即可

这里是通过反射重新赋值来实现,不同版本可能存在差异或者异常,测试用maven依赖

附反射代码:
public static Field getField(Class<?> formType,String fieldName) throws NoSuchFieldException{
if(formType == Object.class)
throw new NoSuchFieldException();
Field[] fields = formType.getDeclaredFields();
for(int i=0;i<fields.length;i++){
if(fields[i].getName().equals(fieldName))
return fields[i];
}
return getField(formType.getSuperclass(),fieldName);
}
@SuppressWarnings("unchecked")
public static <T> T getFieldValue(Object srcObject,String fieldName,Class<T> fieldType){
try {
Field field = getField(srcObject.getClass(),fieldName);
field.setAccessible(true);
return (T) field.get(srcObject);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void setFieldValue(Object srcObject,String fieldName,Object fieldValue){
try {
Field field = getField(srcObject.getClass(),fieldName);
field.setAccessible(true);
field.set(srcObject, fieldValue);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
最后这个问题如能解决,麻烦留言下解决的代码

基于注解实现jackson动态JsonProperty的更多相关文章
- springAOP实现基于注解的数据源动态切换
需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...
- SPRINGAOP实现基于注解的数据源动态切换(转)
需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...
- spring boot整合mybatis基于注解开发以及动态sql的使用
让我们回忆一下上篇博客中mybatis是怎样发挥它的作用的,主要是三类文件,第一mapper接口,第二xml文件,第三全局配置文件(application.properties),而今天我们就是来简化 ...
- spring中实现基于注解实现动态的接口限流防刷
本文将介绍在spring项目中自定义注解,借助redis实现接口的限流 自定义注解类 import java.lang.annotation.ElementType; import java.lang ...
- Struts2基于注解的Action配置
使用注解来配置Action的最大好处就是可以实现零配置,但是事务都是有利有弊的,使用方便,维护起来就没那么方便了. 要使用注解方式,我们必须添加一个额外包:struts2-convention-plu ...
- 基于注解的Spring AOP的配置和使用
摘要: 基于注解的Spring AOP的配置和使用 AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不 ...
- SpringMVC框架搭建 基于注解
本文将以一个很简单的案例实现 Springmvc框架的基于注解搭建,一下全为个人总结 ,如有错请大家指教!!!!!!!!! 第一步:创建一个动态web工程(在创建时 记得选上自动生成 web.xml ...
- Spring AOP:面向切面编程,AspectJ,是基于注解的方法
面向切面编程的术语: 切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象 通知(Advice): 切面必须要完成的工作 目标(Target): 被通知的对象 代理(Pr ...
- Spring基础知识之基于注解的AOP
背景概念: 1)横切关注点:散布在应用中多处的功能称为横切关注点 2)通知(Advice):切面完成的工作.通知定了了切面是什么及何时调用. 5中可以应用的通知: 前置通知(Before):在目标方法 ...
随机推荐
- 基于Linux的系统排错
1.系统引导过程概述 2.系统异常及恢复 [1]grub系统引导 1)mbr上446字节丢失 模拟问题: dd if=/dev/zero? of=/dev/vda? bs=446? count=1? ...
- RabbitMQ-初见
目录 什么是中间件 消息队列协议 AMQP协议 MQTT协议 OpenMessage协议 Kafka协议 消息队列持久化 消息的分发策略 消息队列高可用和高可靠 什么是高可用机制 集群模式1 - Ma ...
- vue+Element-ui 的 el-cascader 做高德地图的省市区三级联动并且是异步加载,点击省市区跳转到对应的区(地图可以通过后端返回的点进行标点)
// 完整版高德地图,可以复制代码直接使用 index.html <script type="text/javascript" src="https://webap ...
- CSP 2021 游记
\(\text{Day -INF}\) 看见了 \(\text{SCP2021}\) 的报名通知,想着应该教练会让我们统一报名,就没放在心上 然后-- 然后过了二十多天教练根本没有提报名的事情,搞得我 ...
- Python - 通过PyYaml库操作YAML文件
PyYaml简单介绍 Python的PyYAML模块是Python的YAML解析器和生成器 它有个版本分水岭,就是5.1 读取YAML5.1之前的读取方法 def read_yaml(self, pa ...
- 第二课:启动 GDB 调试
使用 GDB 调试程序一般有三种方式: gdb filename gdb attach pid gdb filename corename 这也对应着本节课的核心内容: 直接调试目标程序 附加进程 调 ...
- WebDriverAgent重签名爬坑记
接上一篇博文,已经配置好了Xcode环境,那接下来要完成的就是重签名WebDriverAgent.在讲重签名之前,我们还是先来了解下WebDriverAgent,熟悉的朋友,可以直接跳过. WebDr ...
- 微信小程序开发正常,真机预览模式错误
原因是开发时设置了不检查域名是否合法,去后台设置上就可以了
- ABP 极简入门教程(二 MVC方式显示数据)
增加显示菜单 Sample.Web.MVC项目中找到startup目录打开SampleNavigationProvider.cs,根据现有内容添加以下内容 .AddItem( new MenuItem ...
- python库--tensorflow--io操作
方法 返回值类型 参数 说明 .train.Saver() 实例s var_list=None 指定被保存和恢复的变量 dict: {name: 变量} list: [变量] None: 所有save ...