Spring MVC中使用FastJson自定义注解
最近在做.net转译成Java。其中遇到一个很蛋疼的问题。以前.net属性名都是首字母大写。造成返回给客户端的JSON字符串属性名称都是首字母大写。为了和前端对接我们以前都是如下图所示做法
public class User { @JSONField(name = "Name")
private String name;
@JSONField(name = "Age")
private BigDecimal age;
@JSONField(name = "Id")
private String id;
@JSONField(name = "isGirl")
private boolean girl; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public BigDecimal getAge() {
return age;
} public void setAge(BigDecimal age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public boolean isGirl() {
return girl;
} public void setGirl(boolean girl) {
this.girl = girl;
}
}
在每个属性上加上JSONField来定义属性名称,特别的繁琐而且还容易出错。下面我将使用FastJson的自定义注解,通过一个注解来实现。
首先用过继承 WebMvcConfigurationSupport 类来实现一个自定义配置类
package com.raiden; import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.raiden.filter.DataToStringFilter;
import com.raiden.filter.FirstLetterCapitalizedFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List; @Configuration
public class ExtWebMvcConfigurerAdapter extends WebMvcConfigurationSupport { protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//new一个自定义的转换器
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonMessageConverter();
//过滤器链 其中2个是自定义的过滤器
SerializeFilter[] filters = {new FirstLetterCapitalizedFilter(), new DataToStringFilter()};
//将过滤器链放入自定义转换器中
fastJsonHttpMessageConverter.getFastJsonConfig().setSerializeFilters(filters);
//将转换器放入转换器链中
converters.add(fastJsonHttpMessageConverter);
//将转换器链放入配置管理器中
super.configureMessageConverters(converters);
}
}
下面是自定义转换器 其实很简单都不用做什么,只要简单的继承下就好了
package com.raiden; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; /**
*自定义转换器
*/
public class FastJsonMessageConverter extends FastJsonHttpMessageConverter { }
下面是2个自定义的过滤器
如果要处理属性名称则继承NameFilter
一下代码进行了第二次修订,主要是为了防止和JSONField注解冲突
package com.raiden.fastjson.filter; import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.NameFilter;
import com.raiden.fastjson.util.FieldNameUtils;
import com.raiden.fastjson.annotation.FirstLetterCapitalized;
import com.raiden.fastjson.annotation.Ignore;
import com.raiden.fastjson.util.FieldUtils;
import org.springframework.util.StringUtils; import java.lang.reflect.Field; /**
* @创建人:Raiden
* @Descriotion:该过滤器针对属性名,首字母大写过滤器
* @Date:Created in 9:54 2019/6/22
* @Modified By:
*/
public class FirstLetterCapitalizedFilter implements NameFilter {
@Override
public String process(Object instance, String name, Object value) {
if (null == instance || StringUtils.isEmpty(name)){
return name;
}
Class<?> clazz = instance.getClass();
//判断类上是否有首字母大写的注解
if (clazz.isAnnotationPresent(FirstLetterCapitalized.class)){
//是否是boolean实例
boolean isBooleanInstance = Boolean.class.isInstance(value);
//通过名称获得改域 如果使用了JSONField自定义域名会出现找不到的情况
Field field = FieldUtils.getField(clazz, name);
if (null != field){
//看看域上是否有忽略的注解和JSONField注解 或者有 忽略字段注解 如果有则不改变其属性名
if (field.isAnnotationPresent(Ignore.class) || field.isAnnotationPresent(JSONField.class)){
return name;
}else{
//判断下是不是布尔值 如果是切name不是以is开头的 首字母大写并在前面加上is
if (isBooleanInstance && !name.toLowerCase().startsWith("is")){
return "Is" + FieldNameUtils.firstLetterCapitalized(name);
}
//将属性名首字母大写返回
return FieldNameUtils.firstLetterCapitalized(name);
}
}
//用JSONField自定义属性名称可能会找不到域 因此忽略此报错 返回自定义的名称就行
return checkBoolean(clazz, name, isBooleanInstance);
}
return name;
} private String checkBoolean(Class<?> clazz, String name,boolean isBooleanInstance){
if (isBooleanInstance){
//布尔值找不到域 存在2种可能1是用了JSONField注解 2 是使用了小写的is开头 如 isShow 这里的name会是show
String fieldName = "is" + FieldNameUtils.firstLetterCapitalized(name);
//所以拼装好名字之后 在尝试找一次域
Field field = FieldUtils.getField(clazz, fieldName);
//如果找到了返回 带is的
if (null != field){
return fieldName;
}
}
//如果还是获取不到证明使用的是 JSONField注解
return name;
}
}
如果要处理属性内容 则继承 ValueFilter 有时候会遇到BigDecimal 中放入数字 导致序列化之后精度丢失 比如 new BigDecimal(113.880) 序列化之后成了 113.8799999999999954525264911353588104248046875
每个单独处理很麻烦。所以设计了该方式:
package com.raiden.fastjson.filter; import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.raiden.fastjson.util.FieldNameUtils;
import com.raiden.fastjson.annotation.DataToString;
import com.raiden.fastjson.annotation.FirstLetterCapitalized;
import com.raiden.fastjson.util.FieldUtils;
import org.springframework.util.StringUtils; import java.lang.reflect.Field;
import java.math.BigDecimal;
/**
* @创建人:Raiden
* @Descriotion:自定义BigDecimal序列化,精度值处理过滤器
* @Date:Created in 9:54 2019/6/22
* @Modified By:
*/
public class DataToStringFilter implements ValueFilter {
@Override
public Object process(Object instance, String name, Object value) {
if (null == instance || StringUtils.isEmpty(name) || null == value){
return value;
}
//判断下实例是不是BigDecimal 或者是 Double
if (value instanceof Double || value instanceof BigDecimal){
Class<?> instanceClazz = instance.getClass();
//如果存在这个注解说明类名可能被更改
if (instanceClazz.isAnnotationPresent(FirstLetterCapitalized.class)){
name = FieldNameUtils.firstLetterLowercase(name);
}
//如果是则获取该域 如果使用了JSONField自定义域名会出现找不到报错的情况
Field field = FieldUtils.getField(instanceClazz, name);
if (null == field){
field = getField(instanceClazz, name);
}
//检查该域是否有 DataToString注解
if (null != field && field.isAnnotationPresent(DataToString.class)){
return valueFormat(value, field);
}
}
return value;
} /**
* 属性格式化
* @param value
* @param field
* @return
*/
private Object valueFormat(Object value,Field field){
//获取DataToString注解
DataToString dataToString = field.getAnnotation(DataToString.class);
//获取保留小数位
int newScale = dataToString.newScale();
//获取舍入策略
int roundingMode = dataToString.roundingMode();
if (value instanceof Double){
return new BigDecimal((Double) value).setScale(newScale, roundingMode).toString();
}
//返回保留值
return ((BigDecimal) value).setScale(newScale, roundingMode).toString();
} /**
* 获取真正的属性
* @param instanceClazz
* @param name
* @return
*/
private Field getField(Class<?> instanceClazz,String name){
Class<?> superclass = instanceClazz.getSuperclass();
if (null == superclass){
//父类为空证明该类为Object 不递归了返回吧
return null;
}
//遍历全部的域
Field[] fields = instanceClazz.getDeclaredFields();
for (Field field : fields){
if (!field.isAnnotationPresent(JSONField.class)){
continue;
}
JSONField jsonField = field.getAnnotation(JSONField.class);
if (name.equals(jsonField.name())){return field;
}
}
return getField(superclass, name);
}
}
属性名称工具类:
package com.raiden.fastjson; /**
* @创建人:Raiden
* @Descriotion: 属性名称工具类
* @Date:Created in 21:26 2019/6/23
* @Modified By:
*/
public class FieldNameUtils { /**
* 首字母大写的方法
* @param name
* @return
*/
public static String firstLetterCapitalized(String name){
char[] chars = name.toCharArray();
StringBuilder builder = new StringBuilder();
char c = chars[];
//如果是小写才替换
if (c > && c < ){
c -= ;
chars[] = c; }
builder.append(chars);
return builder.toString();
} /**
* 首字母小写
* @param name
* @return
*/
public static String firstLetterLowercase(String name){
char[] chars = name.toCharArray();
StringBuilder builder = new StringBuilder();
char c = chars[];
//如果是小写才替换
if (c > && c < ){
c += ;
chars[] = c; }
builder.append(chars);
return builder.toString();
}
}
package com.raiden.fastjson.util; import java.lang.reflect.Field; /**
* @创建人:Raiden
* @Descriotion:
* @Date:Created in 23:11 2019/7/4
* @Modified By:
*/
public class FieldUtils { /**
* 递归获取域 子类找不到找父类 直到直到或者 递归到Object为止
* @param clazz
* @param fieldName
* @return
*/
public static Field getField(Class<?> clazz, String fieldName){
//获取父类class
Class<?> superclass = clazz.getSuperclass();
if (null == superclass){
//父类为空证明该类为Object 不递归了返回吧
return null;
}
Field declaredField = null;
try {
//忽略报错
declaredField = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
//此处忽略报错 递归查找
return getField(superclass, fieldName);
}
//找到了返回
return declaredField;
}
}
下面是注解部分
package com.raiden.annotation; import java.lang.annotation.*; /**
* 该注解的作用是让FastJson序列化的时候 将所有熟悉的首字母大写
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstLetterCapitalized {
}
package com.raiden.annotation; import java.lang.annotation.*;
import java.math.BigDecimal; /**
* 用于解决BigDecimal序列化精度问题
* 将BigDecimal转成String
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface DataToString {
//默认保留3位小数
int newScale() default ;
//默认使用四舍五入
int roundingMode() default BigDecimal.ROUND_HALF_UP;
}
package com.raiden.annotation; import java.lang.annotation.*; /**
* 忽略该属性注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Ignore {
}
测试代码:
package com.raiden.controller; import com.raiden.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController
public class UserController { @GetMapping("getUser")
public User getUser(){
User user = new User();
user.setId("");
user.setName("zhangsan");
user.setAge(new BigDecimal(113.880));
return user;
}
}
package com.raiden.model; import com.alibaba.fastjson.annotation.JSONField;
import com.raiden.annotation.DataToString;
import com.raiden.annotation.FirstLetterCapitalized;
import com.raiden.annotation.Ignore;
import com.raiden.annotation.Range; import java.math.BigDecimal; @FirstLetterCapitalized
public class User { @Ignore
private String name;
@DataToString(newScale = ,roundingMode = BigDecimal.ROUND_HALF_UP)
private BigDecimal age;
@JSONField(name = "userId")
private String id;
private boolean girl; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public BigDecimal getAge() {
return age;
} public void setAge(BigDecimal age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public boolean isGirl() {
return girl;
} public void setGirl(boolean girl) {
this.girl = girl;
}
}
package com.raiden; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
public class App { public static void main(String[] arg){
SpringApplication.run(App.class, arg);
}
}
第一次写博客,有什么问题还望大佬们指正。代码多次修改如果跑不起来 可以去GitHub下载代码。谢谢
附上github连接:https://github.com/RaidenXin/FastJsonDemo/tree/master
Spring MVC中使用FastJson自定义注解的更多相关文章
- Spring MVC中注解的简介
参考网址: https://blog.csdn.net/a67474506/article/details/46361195 @RequestMapping映射请求 SpringMVC 使用 @Re ...
- Spring MVC 4常用的那些注解
Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...
- [转]Spring MVC 4常用的那些注解
Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...
- 详解Spring MVC 4常用的那些注解
Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...
- Spring MVC 中的基于注解的 Controller【转】
原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...
- Spring MVC中基于注解的 Controller
终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...
- Spring MVC 中的基于注解的 Controller(转载)
终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...
- Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法
Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法 在Action中方法的返回值都是字符串行,一般情况是返回某个JSP,如: return "xx" ...
- Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用
Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/1019 ...
随机推荐
- liunx 系统 一键安装
本文转自:http://hi.baidu.com/iamcyh/item/e777eb81ba90ed5a26ebd9b0 linux VPS环境(MySQL/Apache/PHP/Nginx)一键安 ...
- 【全面解禁!真正的Expression Blend实战开发技巧】第三章 从最常用ButtonStyle开始 - TextButton
原文:[全面解禁!真正的Expression Blend实战开发技巧]第三章 从最常用ButtonStyle开始 - TextButton 在实际项目中,使用blend做的最多的一定是各种自定义But ...
- SimpleMembership,成员资格提供程序、 通用的提供者和新的 ASP.NET 4.5 Web 窗体和 ASP.NET MVC 4 模板
ASP.NET MVC 4 互联网模板中添加一些新的. 非常有用的功能,构建 SimpleMembership.这些更改将添加一些很有特色,像很多更简单. 可扩展会员 API 和 OAuth 的支持. ...
- log4j-slf4j 典型用例
一.maven 配置 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j ...
- 使用PNG实现半透明的窗体(使用GDI+)
Delphi中标准控件是不支持png图片的,据说从Window2000后增加gdiplus.dll库处理更多的gdi图像,其中包括png. 关键的几个api GdipCreateBitma ...
- 一键增加swap空间脚本
#!/bin/bash echo -e "\033[33m Your current swap is \033[0m" free -h mkdir /SwapDir cd /Swa ...
- 最近公共祖先(least common ancestors algorithm)
lca问题是最近公共祖先问题,一般是针对树结构的.现在有两种方法来解决这样的问题 1. On-line algorithm 用比较长的时间做预处理.然后对每次询问进行回答. 思路:对于一棵树中的两个节 ...
- Hadoop集群(第3期)机器信息分布表
1.分布式环境搭建 采用4台安装Linux环境的机器来构建一个小规模的分布式集群. 图1 集群的架构 其中有一台机器是Master节点,即名称节点,另外三台是Slaver节点,即数据节点.这四台机器彼 ...
- 使用 Cake 推送 NuGet 包到 AzureDevops 的 Artifacts 上
前言 大家好,我最近在想如何提交代码的时候自动的打包 NuGet 然后发布到 AzureDevOps 中的 Artifacts,在这个过程中踩了很多坑,也走了很多弯路,所以这次篇文章就是将我探索的结果 ...
- net开发框架never
[一] 摘要 never是纯c#语言开发的一个框架,同时可在netcore下运行. 该框架github地址:https://github.com/shelldudu/never 同时,配合never_ ...