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的自定义注解,通过一个注解来实现。

首先用过继承 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

package com.raiden.filter;

import com.alibaba.fastjson.serializer.NameFilter;
import com.raiden.annotation.FirstLetterCapitalized;
import com.raiden.annotation.Ignore;
import com.raiden.annotation.Range;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;

/**
 *该过滤器针对属性名
 * 首字母大写过滤器
 */
public class FirstLetterCapitalizedFilter implements NameFilter {
    @Override
    public String process(Object o, String name, Object o1) {
        Class<?> clazz = o.getClass();
        //判断下是不是布尔值 如果是首字母大写并在前面加上is
        String fieldName = Boolean.class.isInstance(o1)? "is" + firstLetterCapitalized(name) : name;
        //判断类上是否有首字母大写的注解
        if (clazz.isAnnotationPresent(FirstLetterCapitalized.class)){
            //获取注解
            FirstLetterCapitalized firstLetterCapitalized = clazz.getAnnotation(FirstLetterCapitalized.class);
            //获取注解的属性 Range.ALL指的是全部
            if (firstLetterCapitalized.value() == Range.ALL){
                return firstLetterCapitalized(fieldName);
            }else {
                //如果只是部分属性
                try {
                    //通过名称获得改域
                    Field field = o.getClass().getDeclaredField(name);
                    //看看域上是否有忽略的注解 如果有则不改变其属性名
                    if (!field.isAnnotationPresent(Ignore.class)){
                        //将属性名首字母大写返回
                        return firstLetterCapitalized(fieldName);
                    }
                } catch (NoSuchFieldException e) {
                    //用JSONField自定义属性名称可能会找不到域 因此忽略此报错
                }
            }
        }
        return fieldName;
    }

    /**
     * 首字母大写的方法
     * @param name
     * @return
     */
    private String firstLetterCapitalized(String name){
        if (StringUtils.isEmpty(name)){
            return null;
        }
        char[] chars = name.toCharArray();
        StringBuilder builder = new StringBuilder();
        char c = chars[0];
        c -= 32;
        chars[0] = c;
        builder.append(chars);
        return builder.toString();
    }

}

如果要处理属性内容 则继承 ValueFilter 有时候会遇到BigDecimal 中放入数字 导致序列化之后精度丢失 比如  new BigDecimal(113.880) 序列化之后成了 113.8799999999999954525264911353588104248046875

每个单独处理很麻烦。所以设计了该方式

package com.raiden.filter;

import com.alibaba.fastjson.serializer.ValueFilter;
import com.raiden.annotation.DataToString;

import java.lang.reflect.Field;
import java.math.BigDecimal;

/**
 *自定义BigDecimal序列化 精度值处理过滤器
 */
public class DataToStringFilter implements ValueFilter {
    @Override
    public Object process(Object instance, String name, Object value) {
        //判断下实例是不是BigDecimal
        if (value instanceof BigDecimal){
            try {
                //如果是则获取该域
                Field field = instance.getClass().getDeclaredField(name);
                //检查该域是否有 DataToString注解
                if (field.isAnnotationPresent(DataToString.class)){
                    //获取DataToString注解
                    DataToString dataToString = field.getAnnotation(DataToString.class);
                    //获取保留小数位 
                    int newScale = dataToString.newScale();
                    //获取舍入策略
                    int roundingMode = dataToString.roundingMode();
                    //返回保留值
                    return ((BigDecimal) value).setScale(newScale, roundingMode).toString();
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return value;
    }
}

下面是注解部分

package com.raiden.annotation;

import java.lang.annotation.*;

/**
 * 该注解的作用是让FastJson序列化的时候 将所有熟悉的首字母大写
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FirstLetterCapitalized {

    //作用范围的属性 默认是全部属性值
    Range value() default Range.ALL;
}
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.*;

/**
 * 忽略该属性注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Ignore {
}
package com.raiden.annotation;

public enum  Range {
    ALL,//全部
    PART;//部分
}

测试代码:

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(Range.PART)
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连接:https://github.com/RaidenXin/FastJsonDemo/tree/master

猜你喜欢

转载自www.cnblogs.com/Raiden-xin/p/11068684.html