目前支持自定义枚举字典(Enum)、数字(int、long、float、double)及其包装类、BIgDecimal、List。
后期添加对Date、LocalDate及LocalDateTime的支持。
import cn.hutool.core.codec.Base64;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.muchenx.dict.IDict;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
/**
* 标准Gson序列化/反序列工具
* <p>
* 支持自定义枚举类型,定义的枚举必须实现{@linkplain IDict}接口
* <p>
* 支持处理嵌套集合对象
* <p>
* 为避免报文与Java Bean的属性不匹配,建议搭配注解{@linkplain com.google.gson.annotations.SerializedName}一起使用
* 该注解支持多种备选的属性名称备选方案,因此,可完美解决多端同一个字段而报文中字段名不一致的问题:
* 即Java Bean中是同一个属性,但报文中会出现不同名称。
* 样例:
* <pre>
* <code>
* class User {
* @com.google.gson.annotations.SerializedName(value = "user_name", alternate = {"userName", "USER_NAME"})
* private String username;
* }
* </code>
* 在序列化时,报文中的user_name,userName,USER_NAME均可反序列化到User#username
* </pre>
*/
public final class GsonUtil {
public static Gson gson() {
GsonBuilder builder = new GsonBuilder()
.disableHtmlEscaping()
.serializeNulls()
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
.enableComplexMapKeySerialization()
.registerTypeAdapterFactory(new ApiDictEnumTypeAdapterFactory())
.registerTypeAdapter(byte[].class, new Base64TypeAdapter())
.registerTypeAdapter(List.class, new ListTypeAdapter());
digitalTypeAdapter(builder);
return builder.create();
}
/**
* List适配器
*/
private static class ListTypeAdapter implements JsonDeserializer<List<?>> {
@Override
public List<?> deserialize(JsonElement json, Type type, JsonDeserializationContext ctx) throws JsonParseException {
Type actualType = getGenericType(type);
if (actualType == null) {
return null;
}
if (json.isJsonArray()) {
JsonArray jsonArray = json.getAsJsonArray();
List<?> list = new ArrayList<>(jsonArray.size());
for (JsonElement element : jsonArray) {
list.add(ctx.deserialize(element, actualType));
}
return list;
} else if (json.isJsonObject()) {
return Collections.singletonList(ctx.deserialize(json.getAsJsonObject(), actualType));
}
return null;
}
}
/**
* Base64适配器
*/
private static class Base64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
String value = json.getAsString();
return isnull(value) ? null : Base64.decode(value);
}
@Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Base64.encode(src));
}
}
/**
* 数字适配器
*/
private static void digitalTypeAdapter(GsonBuilder builder) {
builder.registerTypeAdapter(int.class, (JsonDeserializer<Integer>) (json, type, ctx) -> isnull(json.getAsString()) ? 0 : Integer.parseInt(json.getAsString()));
builder.registerTypeAdapter(long.class, (JsonDeserializer<Long>) (json, type, ctx) -> isnull(json.getAsString()) ? 0L : Long.parseLong(json.getAsString()));
builder.registerTypeAdapter(double.class, (JsonDeserializer<Double>) (json, type, ctx) -> isnull(json.getAsString()) ? 0D : Double.parseDouble(json.getAsString()));
builder.registerTypeAdapter(float.class, (JsonDeserializer<Float>) (json, type, ctx) -> isnull(json.getAsString()) ? 0F : Float.parseFloat(json.getAsString()));
builder.registerTypeAdapter(Integer.class, (JsonDeserializer<Integer>) (json, type, ctx) -> isnull(json.getAsString()) ? null : Integer.parseInt(json.getAsString()));
builder.registerTypeAdapter(Long.class, (JsonDeserializer<Long>) (json, type, ctx) -> isnull(json.getAsString()) ? null : Long.parseLong(json.getAsString()));
builder.registerTypeAdapter(Double.class, (JsonDeserializer<Double>) (json, type, ctx) -> isnull(json.getAsString()) ? null : Double.parseDouble(json.getAsString()));
builder.registerTypeAdapter(Float.class, (JsonDeserializer<Float>) (json, type, ctx) -> isnull(json.getAsString()) ? null : Float.parseFloat(json.getAsString()));
builder.registerTypeAdapter(BigDecimal.class, (JsonSerializer<BigDecimal>) (src, type, ctx) -> isnull(src) ? JsonNull.INSTANCE : new JsonPrimitive(src.toPlainString()));
builder.registerTypeAdapter(BigDecimal.class, (JsonDeserializer<BigDecimal>) (json, type, ctx) -> {
String val = json.getAsString();
if (isnull(val)) {
return null;
}
// 去掉特殊字符及空格
val = val.replaceAll("[¥¥$€£,,\\s]+", "");
char c = val.charAt(val.length() - 1);
if (Character.isDigit(c)) {
return new BigDecimal(val);
}
BigDecimal decimal = new BigDecimal(val.substring(0, val.length() - 1));
int dotIdx = val.indexOf('.');
int scale = 0;// 默认小数位数
if (dotIdx != -1) {
// 原小数位数
scale = val.substring(dotIdx, val.length() - 2).length();
}
switch (c) {
case '%':
case '%':
return decimal.divide(new BigDecimal("100"), scale + 2, RoundingMode.HALF_UP);
case '‰':
return decimal.divide(new BigDecimal("1000"), scale + 3, RoundingMode.HALF_UP);
}
// 无效数字,返回null
return null;
});
}
// 通用自定义枚举序列化/反序列化适配器
@SuppressWarnings("all")
private static class ApiDictEnumTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<?> rawType = type.getRawType();
if (!rawType.isEnum() || !IDict.class.isAssignableFrom(rawType)) {
return null;
}
return new ApiDictAdapter<>((Class<T>) rawType);
}
private static class ApiDictAdapter<T> extends TypeAdapter<T> {
private final Class<T> enumType;
private final Map<Object, T> dictMap = new HashMap<>();
public ApiDictAdapter(Class<T> enumType) {
this.enumType = enumType;
Arrays.stream(enumType.getEnumConstants()).forEach(e -> dictMap.put(((IDict<?, ?>) e).getCode(), e));
}
@Override
public void write(JsonWriter out, T value) throws IOException {
if (isnull(value)) {
out.nullValue();
return;
}
out.value((String) ((IDict<?, ?>) value).getCode());
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String value = in.nextString();
T dict = dictMap.get(value);
return Optional.ofNullable(dict).orElseThrow(() -> new IOException("value[" + value + "] does not defined in[" + enumType + "]"));
}
}
}
private static boolean isnull(Object value) {
if (value instanceof CharSequence) {
return "null".equalsIgnoreCase((String) value) || !StringUtils.hasText((CharSequence) value);
}
return null == value;
}
/**
* 获取泛型参数类型(没找到返回null)
*/
public static Type getGenericType(Type cls) {
if (cls instanceof ParameterizedType) {
Type[] types = ((ParameterizedType) cls).getActualTypeArguments();
return isnull(types) ? null : types[0];
}
return null;
}
}