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