最近在做.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. liunx 系统 一键安装

    本文转自:http://hi.baidu.com/iamcyh/item/e777eb81ba90ed5a26ebd9b0 linux VPS环境(MySQL/Apache/PHP/Nginx)一键安 ...

  2. 【全面解禁!真正的Expression Blend实战开发技巧】第三章 从最常用ButtonStyle开始 - TextButton

    原文:[全面解禁!真正的Expression Blend实战开发技巧]第三章 从最常用ButtonStyle开始 - TextButton 在实际项目中,使用blend做的最多的一定是各种自定义But ...

  3. SimpleMembership,成员资格提供程序、 通用的提供者和新的 ASP.NET 4.5 Web 窗体和 ASP.NET MVC 4 模板

    ASP.NET MVC 4 互联网模板中添加一些新的. 非常有用的功能,构建 SimpleMembership.这些更改将添加一些很有特色,像很多更简单. 可扩展会员 API 和 OAuth 的支持. ...

  4. log4j-slf4j 典型用例

    一.maven 配置 <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j ...

  5. 使用PNG实现半透明的窗体(使用GDI+)

      Delphi中标准控件是不支持png图片的,据说从Window2000后增加gdiplus.dll库处理更多的gdi图像,其中包括png.   关键的几个api   GdipCreateBitma ...

  6. 一键增加swap空间脚本

    #!/bin/bash echo -e "\033[33m Your current swap is \033[0m" free -h mkdir /SwapDir cd /Swa ...

  7. 最近公共祖先(least common ancestors algorithm)

    lca问题是最近公共祖先问题,一般是针对树结构的.现在有两种方法来解决这样的问题 1. On-line algorithm 用比较长的时间做预处理.然后对每次询问进行回答. 思路:对于一棵树中的两个节 ...

  8. Hadoop集群(第3期)机器信息分布表

    1.分布式环境搭建 采用4台安装Linux环境的机器来构建一个小规模的分布式集群. 图1 集群的架构 其中有一台机器是Master节点,即名称节点,另外三台是Slaver节点,即数据节点.这四台机器彼 ...

  9. 使用 Cake 推送 NuGet 包到 AzureDevops 的 Artifacts 上

    前言 大家好,我最近在想如何提交代码的时候自动的打包 NuGet 然后发布到 AzureDevOps 中的 Artifacts,在这个过程中踩了很多坑,也走了很多弯路,所以这次篇文章就是将我探索的结果 ...

  10. net开发框架never

    [一] 摘要 never是纯c#语言开发的一个框架,同时可在netcore下运行. 该框架github地址:https://github.com/shelldudu/never 同时,配合never_ ...