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的自定義註解,經過一個註解來實現。java

首先用過繼承 WebMvcConfigurationSupport 類來實現一個自定義配置類git

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);
    }
}

下面是自定義轉換器 其實很簡單都不用作什麼,只要簡單的繼承下就行了github

package com.raiden;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;

/**
 *自定義轉換器
 */
public class FastJsonMessageConverter extends FastJsonHttpMessageConverter {

}

 

下面是2個自定義的過濾器web

若是要處理屬性名稱則繼承NameFilterspring

一下代碼進行了第二次修訂,主要是爲了防止和JSONField註解衝突json

 

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.8799999999999954525264911353588104248046875api

每一個單獨處理很麻煩。因此設計了該方式:app

 

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[0];
        //若是是小寫才替換
        if (c > 96 && c < 123){
            c -= 32;
            chars[0] = 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[0];
        //若是是小寫才替換
        if (c > 64 && c < 91){
            c += 32;
            chars[0] = 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;
    }
}

 

下面是註解部分ide

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 3;
    //默認使用四捨五入
    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("1");
        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 = 3,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

相關文章
相關標籤/搜索