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