最近在做.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. 【C#】使用EF访问Sqlite数据库

    原文:[C#]使用EF访问Sqlite数据库 1. 先上Nuget下载对应的包 如图,搜索System.Data.SQLite下载安装即可,下载完之后带上依赖一共有这么几个: EntityFramew ...

  2. C++函数不写bool返回值,居然编译运行全部通过,但判断结果就不对了

    bool MyStart::IsCoorectParam(QString strParam) { if (strParam=="-aa" || strParam=="-b ...

  3. Markdown的选择

    直击现场 我一直在思索用什么格式存储文档比较好.之前一直在用google docs,但是它的格式不公开,上传/下载的时候需要转换格式,转换的时候必然会丢失一些信息.于是后来想,那还是纯本文或者mark ...

  4. Windows 64 位下安装 psyco 1.6

    用 eclipse 运行 python 的时候,第一行总是有红色提示:没有安装 psyco,程序可以正常运行但是会有一点慢.于是就干脆装上吧,红色的提示还是越少越舒服. 百度了一下,在这里,http: ...

  5. SYN012型B码时统

       SYN012型B码时统 产品概述 SYN012型B码时统是由西安同步电子科技有限公司精心设计.自行研发生产的一款通用性时统终端,内置高精度恒温晶振,接收GPS北斗双模卫星信号,10MHz外部参考 ...

  6. Java算法-求最大和的子数组序列

    问题:有一个连续数组,长度是确定的,它包含多个子数组,子数组中的内容必须是原数组内容中的一个连续片段,长度不唯一,子数组中每个元素相加的结果称为子数组的和,现要求找出和最大的一个子数组. 具体算法如下 ...

  7. 分享android ADT百度云盘下载地址

    由于android官网经常无法打开,特意把最新的android ADT和SDK放到了百度云盘进行了分享,目录中包含Windows和Macbook两种平台的版本,请自行选择: http://pan.ba ...

  8. Python Re 模块超全解读!

    re模块下的函数 compile(pattern):创建模式对象 import repat=re.compile('A')m=pat.search('CBA')                     ...

  9. Exceptionless(二) - 使用进阶

    Exceptionless(二) - 使用进阶 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/11100563.html 官网 ...

  10. PWN菜鸡入门之栈溢出 (2)—— ret2libc与动态链接库的关系

    准备知识引用自https://www.freebuf.com/articles/rookie/182894.html 0×01 利用思路 ret2libc 这种攻击方式主要是针对 动态链接(Dynam ...