最近在做.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[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;
    }
}

 

下面是注解部分

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