最近在做.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自定义注解的更多相关文章

  1. Spring MVC中注解的简介

    参考网址:  https://blog.csdn.net/a67474506/article/details/46361195 @RequestMapping映射请求 SpringMVC 使用 @Re ...

  2. Spring MVC 4常用的那些注解

    Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...

  3. [转]Spring MVC 4常用的那些注解

    Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...

  4. 详解Spring MVC 4常用的那些注解

    Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam, @ModelAttribute等等这样类似的注解.到目前为止,Spring的版 ...

  5. Spring MVC 中的基于注解的 Controller【转】

    原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...

  6. Spring MVC中基于注解的 Controller

         终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...

  7. Spring MVC 中的基于注解的 Controller(转载)

           终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...

  8. Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法

    Spring MVC 中采用注解方式 Action中跳转到另一个Action的写法 在Action中方法的返回值都是字符串行,一般情况是返回某个JSP,如: return "xx" ...

  9. Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用

    Spring 注解驱动(二)Servlet 3.0 注解驱动在 Spring MVC 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/1019 ...

随机推荐

  1. ES6/ES2015核心内容(转载)

    ES6其实就是ES2015,因为是2015年发布的,所以也叫ES2015.这个版本是JS的最新版本,很多浏览器还不支持,所有有了babel,专门把最新的JS转换一下,让大部分浏览器都支持的JS版本. ...

  2. 队列读取器代理 遇到错误 Row handle is invalid

    原文:队列读取器代理 遇到错误 Row handle is invalid 今天测试在发布中更改表名称,在发布数据库更改后重新发布这个表. 但是原来的表在订阅没有删除,不小心插入数据到原表中,队列读取 ...

  3. 解决WPF中TextBox文件拖放问题

    在WPF中,当我们尝试向TextBox中拖放文件,从而获取其路径时,往往无法成功(拖放文字可以成功).造成这种原因关键是WPF的TextBox对拖放事件处理机制的不同,具体可参考这篇文章Textbox ...

  4. 使用Chart控件进行实时监控

    Chart作为微软提供绘制图表的控件,在刚开始使用时非常的迷茫,因为功能强大,涉及到的知识多, 一开始难以接收过来,但后天经过查找资料,耐心学习,终于还是有了一定的收获. Chart相当于一个大的图纸 ...

  5. kube框架结构-一个小型响应式CSS框架

    当你开始初建一个新的项目时,你可能需要一个不太复杂的基础框架,Kube框架应该是你最好的选择.一个独立的CSS文件,帮助你更简单的创建响应式的的布局设计. Kube Framework包括网格.按钮. ...

  6. 获取函数的地址(三种方法,分别是@,Addr,MethodAddress)

    问题来源: http://www.cnblogs.com/del/archive/2008/07/30/1039045.html#1272783 在编译器看来, 重载函数根本就是完全不同的几个函数, ...

  7. UILabel实现自适应宽高需要注意的地方(三)

        一.需求图如下所示    UILabel 的高度自适应 UILabel中的段落间距可设置   图片效果如下:   调整段落适应长宽高方式:         需求:   保证"游戏玩法 ...

  8. QTableView中加入Check列实现,无需Delegate(使用::data(),Qt原生支持)

    通过Delegate创建QCheckBox来实现的Check列,只有在该列进入编辑模式时才能够Check/Uncheck.这显然不是我们想要的,网上翻来翻去,在一个国外论坛中看到了无需Delegate ...

  9. .NET Core 微服务之Polly熔断策略

    紧接着上一篇说,咱们继续介绍Polly这个类库 熔断策略(Circuit-breaker) 如果调用某个目标服务出现过多超时.异常等情况,可以采取一定时间内熔断该服务的调用,熔断期间的请求将不再继续调 ...

  10. 3017C语言_文件

    第七章  文件 7.1  C语言文件的概念 7.1.1 文件的概念 在此之前,所有的输入和输出只涉及到键盘和显示器.在运行C程序时,我们通过键盘输入数据并借助显示器把程序的运算结果显示出来.但是,计算 ...