写在前面
迫于生计,一直忙于路上,没时间更新。
正好本次有用上用到Gson序列化反序列化工具,且极大程度不满足应用场景,特地对Gson花了两天做了深度定制。记录上,也为了正好需要的朋友提供一些助力,开箱即用!
本次遇到不满足场景及定制化大致如下:1.对于BIgDecimal的反序列化时,数字字符串是千分位分隔符、包含%、‰、‱,原生Gson无法满足;
2.对于自定义枚举值的序列化、反序列化更是复杂化;
3.对与接口JavaBean的双方约定的List类型的属性,数据交换时,如果该数组仅有一个对象时,对方很有可能给你得是一个JSON Object而非JSON Array(反正我是遇上了)。这种场景只能定制化反序列化对List的特殊处理;
4.对于String字符串的序列化/反序列化,对于接口日志记录,如果某个文件转为Base64或Hex传输,但此时记录日志,如果不省略,得不偿失,所以也需要定制化;
5.除此之外,对Map、Date、LocalDate、LocalDateTime、LocalTime、Duration等也做了定制化。
一、所需依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.13.1</version>
</dependency>
二、Gson定制化工具
import com.google.gson.*;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.GsonTypes;
import com.google.gson.internal.ObjectConstructor;
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.kosmian.annotation.IgnoreUnknownEnum;
import com.kosmian.dict.ApiDict;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* Gson序列化/反序列化定制化工具(基于Gson version:2.13.1)
* <p>
* 用到忽略未知枚举值注解
* <pre><code>
* \@Documented
* \@Retention(RetentionPolicy.RUNTIME)
* \@Target(ElementType.FIELD)
* public @interface IgnoreUnknownEnum {}</code></pre>
* 用的枚举字典基类接口
* <pre><code>
* public interface ApiDict<C, T extends Enum<T>> {
* C getCode();
* String getDesc();
* default String[] alternate() {
* return new String[0];
* }
* }</code></pre>
*
* @author Ian
*/
public final class SuperGsonUtil {
private static class Holder {
private static final SuperGsonUtil INSTANCE = new SuperGsonUtil();
}
private static SuperGsonUtil getInstance() {
return Holder.INSTANCE;
}
public static Gson gson() {
return gson(0);
}
public static Gson gson(int retainLength) {
return getInstance().builder(retainLength).create();
}
private GsonBuilder builder(int retainLength) {
GsonBuilder builder = new GsonBuilder()
.setStrictness(Strictness.LENIENT) // 解析Json数据时忽略未知字段
.disableHtmlEscaping()
.disableJdkUnsafe()
.serializeNulls()
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
.enableComplexMapKeySerialization();
// 1.注册枚举字典类型适配器
builder.registerTypeAdapterFactory(new ApiDictTypeAdapterFactory());
// 2.注册String类型适配器工厂
builder.registerTypeAdapterFactory(new StringTypeAdapterFactory(retainLength));
// 3.注册BIgDecimal类型适配器工厂
builder.registerTypeAdapterFactory(new BigDecimalTypeAdapterFactory());
// 4.注册Date相关适配器工厂
builder.registerTypeAdapterFactory(new DateTypeAdapterFactory());
// 5.注册集合适配器工厂
builder.registerTypeAdapterFactory(new CollectionTypeAdapterFactory(retainLength));
// 6.注册自定义Array适配器
builder.registerTypeAdapterFactory(new ArrayTypeAdapterFactory(retainLength));
// 7.注册Map适配器
builder.registerTypeAdapterFactory(new MapTypeAdapterFactory(retainLength));
// 8.注册自定义Object适配器(核心:拦截所有Object类型中的字符串)
builder.registerTypeAdapterFactory(new ObjectTypeAdapterFactory(retainLength));
return builder;
}
private record BigDecimalTypeAdapterFactory() implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (BigDecimal.class.isAssignableFrom(typeToken.getRawType())) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new Adapter();
return adapter;
}
return null;
}
static class Adapter extends TypeAdapter<BigDecimal> {
@Override
public BigDecimal read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String s = in.nextString();
// 去除空格及逗号分隔符
String num = s.trim().replaceAll("[\\s,,]", "");
// 处理.xx的情况为0.xx
num = num.startsWith(".") ? "0" + num : num;
char last = num.charAt(num.length() - 1);
if (Character.isDigit(last)) {
return new BigDecimal(num);
}
// 处理百分号、千分号、万分号
BigDecimal percentDecimal = Stream.of(PercentDecimal.values())
.filter(item -> item.symbol == last)
.findFirst()
.orElseThrow(() -> new JsonSyntaxException("Unexpected symbol [" + last + "] in token: " + s))
.decimal;
return new BigDecimal(num.substring(0, num.length() - 1)).multiply(percentDecimal);
}
@Override
public void write(JsonWriter out, BigDecimal value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(value.toPlainString());
}
}
}
private record DateTypeAdapterFactory() implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
if (Date.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new DateAdapter();
} else if (LocalDate.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new LocalDateAdapter();
} else if (LocalDateTime.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new LocalDateTimeAdapter();
} else if (LocalTime.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new LocalTimeAdapter();
} else if (Duration.class.isAssignableFrom(rawType)) {
return (TypeAdapter<T>) new DurationAdapter();
}
// 其他类型返回null,使用Gson默认适配器
return null;
}
static class LocalTimeAdapter extends TypeAdapter<LocalTime> {
final DateTimeFormatter format = DateTimeFormatter.ofPattern("HH:m:ss");
@Override
public void write(JsonWriter out, LocalTime value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(format.format(value));
}
}
@Override
public LocalTime read(JsonReader in) throws IOException {
if (JsonToken.STRING == in.peek()) {
String val = in.nextString().trim();
LocalTime time = TimePattern.TIME.parse(val);
if (time != null) {
return time;
}
throw new JsonSyntaxException("Unexpected time token: " + val);
}
return null;
}
}
static class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {
final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public void write(JsonWriter out, LocalDateTime value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(format.format(value));
}
}
@Override
public LocalDateTime read(JsonReader in) throws IOException {
if (JsonToken.STRING == in.peek()) {
String val = in.nextString();
for (DatetimePattern p : DatetimePattern.values()) {
LocalDateTime date = p.parse(val.trim());
if (date != null) {
return date;
}
}
throw new JsonSyntaxException("Unexpected datetime token: " + val);
}
return null;
}
}
static class LocalDateAdapter extends TypeAdapter<LocalDate> {
final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public void write(JsonWriter out, LocalDate value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(format.format(value));
}
}
@Override
public LocalDate read(JsonReader in) throws IOException {
if (JsonToken.STRING == in.peek()) {
String val = in.nextString();
for (DatePattern p : DatePattern.values()) {
LocalDate date = p.parse(val.trim());
if (date != null) {
return date;
}
}
throw new JsonSyntaxException("Unexpected date token: " + val);
}
return null;
}
}
static class DurationAdapter extends TypeAdapter<Duration> {
@Override
public void write(JsonWriter out, Duration value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.getSeconds());
}
}
@Override
public Duration read(JsonReader in) throws IOException {
if (JsonToken.NUMBER == in.peek()) {
return Duration.ofSeconds(in.nextLong());
}
return null;
}
}
static class DateAdapter extends TypeAdapter<Date> {
final Pattern defaultPattern = Pattern.compile(".*?(?i)(AM|PM)$");
final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
final ZoneId defaultZoneId = ZoneId.systemDefault();
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(format.format(value));
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (JsonToken.STRING == in.peek()) {
String val = in.nextString().trim();
LocalDateTime date = null;
for (DatetimePattern p : DatetimePattern.values()) {
date = p.parse(val);
if (date != null) {
break;
}
}
if (date != null) {
return Date.from(date.atZone(defaultZoneId).toInstant());
}
if (defaultPattern.matcher(val).matches()) {
try {
return DateFormat.getDateInstance().parse(val);
} catch (ParseException e) {
throw new JsonSyntaxException("Unexpected datetime token: " + val);
}
}
}
return null;
}
}
}
private record ApiDictTypeAdapterFactory() implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<?> rawType = type.getRawType();
if (ApiDict.class.isAssignableFrom(rawType) && rawType.isEnum()) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new Adapter<>((Class<T>) rawType);
return adapter;
}
return null;
}
static class Adapter<T> extends TypeAdapter<ApiDict<?, ?>> {
final Class<ApiDict<?, ?>> enumType;
@SuppressWarnings("unchecked")
Adapter(Class<T> enumType) {
this.enumType = (Class<ApiDict<?, ?>>) enumType;
}
@Override
public void write(JsonWriter out, ApiDict value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
switch (value.getCode()) {
case String v -> out.value(v);
case Boolean b -> out.value(b);
case Number v -> out.value(v);
case null -> out.nullValue();
default -> out.value(String.valueOf(value.getCode()));
}
}
@Override
public ApiDict<?, ?> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return convert(enumType, in.nextString().trim(), enumType.isAnnotationPresent(IgnoreUnknownEnum.class));
}
ApiDict<?, ?> convert(Class<ApiDict<?, ?>> enumCls, Object code, boolean ignoreUnknown) {
Optional.ofNullable(enumCls).orElseThrow(() -> new JsonSyntaxException("未指定枚举类型"));
final String value = String.valueOf(code);
if (value == null || value.trim().isEmpty()) {
return null;
}
ApiDict<?, ?>[] enums = enumCls.getEnumConstants();
// 根据code查找
Optional<ApiDict<?, ?>> optional = Stream.of(enums)
.filter(item -> value.equals(String.valueOf(item.getCode())))
.findFirst();
if (optional.isPresent()) {
return optional.get();
}
// 根据可替代值查找
optional = Stream.of(enums).filter(item -> item.alternate() != null)
.filter(item -> Arrays.asList(item.alternate()).contains(value))
.findFirst();
if (optional.isPresent()) {
return optional.get();
}
// 根据Enum.name查找
optional = Stream.of(enums).filter(item -> ((Enum<?>) item).name().equals(value))
.findFirst();
if (optional.isPresent()) {
return optional.get();
}
// 根据Enum.ordinal查找
try {
return enums[Integer.parseInt(value)];
} catch (Exception ex) {
if (ignoreUnknown) {
return null;
}
throw new JsonSyntaxException(String.format("枚举字典[%s]中未定义枚举值[%s]", enumCls.getSimpleName(), code));
}
}
}
}
private record MapTypeAdapterFactory(int retainLength) implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (!Map.class.isAssignableFrom(typeToken.getRawType())) {
return null;
}
TypeAdapter<?> valueAdapter = new ObjectTypeAdapterFactory.Adapter(gson, retainLength);
@SuppressWarnings({"unchecked", "rawtypes"})
TypeAdapter<T> adapter = new Adapter(valueAdapter, newConstructor().get(typeToken));
return adapter;
}
static class Adapter<T> extends TypeAdapter<Map<String, T>> {
final TypeAdapter<T> valueAdapter;
final ObjectConstructor<? extends Map<String, T>> constructor;
public Adapter(TypeAdapter<T> valueAdapter, ObjectConstructor<? extends Map<String, T>> constructor) {
this.valueAdapter = valueAdapter;
this.constructor = constructor;
}
@Override
public Map<String, T> read(JsonReader in) throws IOException {
JsonToken token = in.peek();
if (token == JsonToken.NULL) {
in.nextNull();
return null;
}
Map<String, T> map = constructor.construct();
in.beginObject();
while (in.hasNext()) {
String key = in.nextName();
T value = valueAdapter.read(in);
map.put(key, value);
}
in.endObject();
return map;
}
@Override
public void write(JsonWriter out, Map<String, T> map) throws IOException {
if (map == null) {
out.nullValue();
return;
}
out.beginObject();
for (Map.Entry<String, T> entry : map.entrySet()) {
out.name(entry.getKey());
T value = entry.getValue();
valueAdapter.write(out, value);
}
out.endObject();
}
}
}
private record StringTypeAdapterFactory(int retainLength) implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (String.class.isAssignableFrom(typeToken.getRawType())) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new Adapter(retainLength);
return adapter;
}
return null;
}
static class Adapter extends TypeAdapter<String> {
final int retainLength;
Adapter(int retainLength) {
this.retainLength = retainLength;
}
@Override
public String read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
in.nextNull();
return null;
}
if (peek == JsonToken.BOOLEAN) {
return Boolean.toString(in.nextBoolean());
}
// 处理数字类型(可选,根据需求)
if (peek == JsonToken.NUMBER) {
return in.nextString();
}
String str = in.nextString();
return handleValue(retainLength, str);
}
@Override
public void write(JsonWriter out, String value) throws IOException {
out.value(handleValue(retainLength, value));
}
}
}
private record ObjectTypeAdapterFactory(int retainLength) implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
// 匹配所有Object类型(包括泛型中的Object)
if (typeToken.getRawType() == Object.class) {
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new Adapter(gson, retainLength);
return adapter;
}
return null;
}
static class Adapter extends TypeAdapter<Object> {
final Gson gson;
final int retainLength;
Adapter(Gson gson, int retainLength) {
this.gson = gson;
this.retainLength = retainLength;
}
@Override
public Object read(JsonReader in) throws IOException {
JsonToken token = in.peek();
return switch (token) {
case NULL -> {
in.nextNull();
yield null;
}
case BOOLEAN -> in.nextBoolean();
case NUMBER -> {
// 委托给Number适配器
TypeToken<?> typeToken = TypeToken.getParameterized(Number.class);
TypeAdapter<?> typeAdapter = gson.getAdapter(typeToken);
yield typeAdapter.read(in);
}
case STRING -> {
// 委托给String适配器
TypeToken<?> typeToken = TypeToken.getParameterized(String.class);
TypeAdapter<?> typeAdapter = gson.getAdapter(typeToken);
yield typeAdapter.read(in);
}
case BEGIN_ARRAY -> {
// 委托给Collection适配器
TypeToken<?> typeTokenColl = TypeToken.getParameterized(Collection.class, Object.class);
TypeAdapter<?> adapter = gson.getAdapter(typeTokenColl);
yield adapter.read(in);
}
case BEGIN_OBJECT -> {
// 解析对象为Map适配器
TypeToken<?> mapTypeToken = TypeToken.getParameterized(Map.class, String.class, Object.class);
TypeAdapter<?> mapAdapter = gson.getAdapter(mapTypeToken);
yield mapAdapter.read(in);
}
default -> throw new JsonSyntaxException("Unexpected token: " + token);
};
}
@Override
public void write(JsonWriter out, Object value) throws IOException {
switch (value) {
case null -> out.nullValue();
case String str -> {
TypeToken<?> typeToken = TypeToken.getParameterized(String.class);
@SuppressWarnings("unchecked")
TypeAdapter<String> adapter = (TypeAdapter<String>) gson.getAdapter(typeToken);
adapter.write(out, str);
}
case Number num -> {
TypeToken<?> typeToken = TypeToken.getParameterized(Number.class);
@SuppressWarnings("unchecked")
TypeAdapter<Number> adapter = (TypeAdapter<Number>) gson.getAdapter(typeToken);
adapter.write(out, num);
}
case Boolean bool -> out.value(bool);
case ApiDict<?, ?> dict -> out.value(String.valueOf(dict.getCode()));
case Collection<?> coll -> {
TypeToken<?> typeToken = TypeToken.getParameterized(Collection.class, Object.class);
@SuppressWarnings("unchecked")
TypeAdapter<? super Collection<?>> adapter = (TypeAdapter<? super Collection<?>>) gson.getAdapter(typeToken);
adapter.write(out, coll);
}
case Map<?, ?> map -> {
TypeToken<?> typeToken = TypeToken.getParameterized(Map.class, String.class, Object.class);
@SuppressWarnings("unchecked")
TypeAdapter<? super Map<?, ?>> adapter = (TypeAdapter<? super Map<?, ?>>) gson.getAdapter(typeToken);
adapter.write(out, map);
}
default -> gson.toJson(value, value.getClass(), out);
}
}
}
}
private record ArrayTypeAdapterFactory(int retainLength) implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof GenericArrayType || (type instanceof Class && ((Class<?>) type).isArray()))) {
return null;
}
Type componentType = GsonTypes.getArrayComponentType(type);
TypeAdapter<?> elementTypeAdapter = new ObjectTypeAdapterFactory.Adapter(gson, retainLength);
@SuppressWarnings({"unchecked", "rawtypes"})
TypeAdapter<T> adapter = new Adapter(elementTypeAdapter, newConstructor().get(typeToken),
GsonTypes.getRawType(componentType));
return adapter;
}
static class Adapter<E> extends TypeAdapter<Object> {
final TypeAdapter<E> elementTypeAdapter;
final ObjectConstructor<? extends E[]> constructor;
final Class<E> componentType;
Adapter(TypeAdapter<E> elementTypeAdapter, ObjectConstructor<? extends E[]> constructor, Class<E> componentType) {
this.elementTypeAdapter = elementTypeAdapter;
this.constructor = constructor;
this.componentType = componentType;
}
@Override
public Object read(JsonReader in) throws IOException {
JsonToken token = in.peek();
return switch (token) {
case NULL -> {
in.nextNull();
yield null;
}
case BEGIN_ARRAY -> {
ArrayList<E> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
E instance = elementTypeAdapter.read(in);
list.add(instance);
}
in.endArray();
int size = list.size();
if (componentType.isPrimitive()) {
Object array = Array.newInstance(componentType, size);
for (int i = 0; i < size; i++) {
Array.set(array, i, list.get(i));
}
yield array;
} else {
@SuppressWarnings("unchecked")
E[] array = (E[]) Array.newInstance(componentType, size);
yield list.toArray(array);
}
}
case BEGIN_OBJECT, NUMBER, STRING, BOOLEAN -> {
E instance = elementTypeAdapter.read(in);
Object array = Array.newInstance(componentType, 1);
Array.set(array, 0, instance);
yield array;
}
default -> throw new JsonSyntaxException("Expected array or single value but was " + token);
};
}
@Override
public void write(JsonWriter out, Object array) throws IOException {
if (array == null) {
out.nullValue();
return;
}
out.beginArray();
for (int i = 0, length = Array.getLength(array); i < length; i++) {
@SuppressWarnings("unchecked")
E value = (E) Array.get(array, i);
elementTypeAdapter.write(out, value);
}
out.endArray();
}
}
}
private record CollectionTypeAdapterFactory(int retainLength) implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (!Collection.class.isAssignableFrom(typeToken.getRawType())) {
return null;
}
TypeAdapter<?> elementTypeAdapter = new ObjectTypeAdapterFactory.Adapter(gson, retainLength);
@SuppressWarnings({"unchecked", "rawtypes"})
TypeAdapter<T> adapter = new Adapter(elementTypeAdapter, newConstructor().get(typeToken));
return adapter;
}
static class Adapter<E> extends TypeAdapter<Collection<E>> {
final TypeAdapter<E> elementTypeAdapter;
final ObjectConstructor<? extends Collection<E>> constructor;
Adapter(TypeAdapter<E> elementTypeAdapter, ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter = elementTypeAdapter;
this.constructor = constructor;
}
@Override
public Collection<E> read(JsonReader in) throws IOException {
JsonToken token = in.peek();
return switch (token) {
case NULL -> {
in.nextNull();
yield null;
}
case BEGIN_ARRAY -> {
Collection<E> collect = constructor.construct();
in.beginArray();
while (in.hasNext()) {
E instance = elementTypeAdapter.read(in);
collect.add(instance);
}
in.endArray();
yield collect;
}
case BEGIN_OBJECT, NUMBER, STRING, BOOLEAN -> {
Collection<E> collect = constructor.construct();
E instance = elementTypeAdapter.read(in);
collect.add(instance);
yield collect;
}
default -> throw new JsonSyntaxException("Expected array or single value but was " + token);
};
}
@Override
public void write(JsonWriter out, Collection<E> collection) throws IOException {
if (collection == null) {
out.nullValue();
return;
}
out.beginArray();
for (E element : collection) {
elementTypeAdapter.write(out, element);
}
out.endArray();
}
}
}
private static String handleValue(int retainLength, String value) {
if (value == null) {
return null;
}
if (retainLength > 0 && value.length() > retainLength) {
return value.substring(0, retainLength) + "…";
}
return value;
}
enum PercentDecimal {
ONE_PERCENT('%', new BigDecimal("0.01")),
ONE_PERCENT_CN('%', new BigDecimal("0.01")),
ONE_THOUSANDTH('‰', new BigDecimal("0.001")),
ONE_TEN_THOUSANDTH('‱', new BigDecimal("0.0001")),
;
private final char symbol;
private final BigDecimal decimal;
PercentDecimal(char symbol, BigDecimal decimal) {
this.symbol = symbol;
this.decimal = decimal;
}
}
enum DatePattern {
DATE_PATTERN("^(\\d{2}|\\d{4})([-/.])(1[012]|0?[1-9])\\2(3[01]|[12]\\d|0?[1-9])$",
1, 3, 4),
DATE_CN_ZH("^(\\d{2}|\\d{4})年(1[012]|0?[1-9])月(3[01]|[12]\\d|0?[1-9])[日号]$",
1, 2, 3),
DATE_6("^\\d{6}$"),
DATE_8("^\\d{8}$");
private final Pattern p;
private final int[] groups;
DatePattern(String reg, int... groups) {
this.p = Pattern.compile(reg);
this.groups = groups;
}
LocalDate parse(String dateStr) {
Matcher m = p.matcher(dateStr);
if (m.find()) {
return switch (this) {
case DATE_PATTERN, DATE_CN_ZH -> LocalDate.of(
Integer.parseInt(m.group(groups[0])),
Integer.parseInt(m.group(groups[1])),
Integer.parseInt(m.group(groups[2]))
);
case DATE_6 -> LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyMMdd"));
case DATE_8 -> LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
};
}
return null;
}
}
enum TimePattern {
TIME("^(0?\\d|1\\d|2[0123])[时点:](0?[1-9]|[1-5]\\d)[分:](0?[1-9]|[1-5]\\d)秒?$");
private final Pattern p;
TimePattern(String reg) {
this.p = Pattern.compile(reg);
}
LocalTime parse(String timeStr) {
Matcher m = p.matcher(timeStr);
if (m.find()) {
return LocalTime.of(
Integer.parseInt(m.group(1)),
Integer.parseInt(m.group(2)),
Integer.parseInt(m.group(3))
);
}
return null;
}
}
enum DatetimePattern {
DATE_TIME("^(\\d{2}|\\d{4})([-/.])(1[012]|0?[1-9])\\2(3[01]|[12]\\d|0?[1-9])[\\sTt](0?\\d|1\\d|2[0123])(:)(0?[1-9]|[1-5]\\d)\\6(0?[1-9]|[1-5]\\d)$",
1, 3, 4, 5, 7, 8),
DATE_TIME_CN("^(\\d{2}|\\d{4})\\年(1[012]|0?[1-9])月(3[01]|[12]\\d|0?[1-9])[日号][\\sTt]?(0?\\d|1\\d|2[0123])[时点:](0?[1-9]|[1-5]\\d)[分:](0?[1-9]|[1-5]\\d)秒?$",
1, 2, 3, 4, 5, 6),
DATE_TIME_6("^(\\d{6})[\\sTt](0?\\d|1\\d|2[0123])(:)(0?[1-9]|[1-5]\\d)\\3(0?[1-9]|[1-5]\\d)$",
1, 2, 4, 5),
DATE_TIME_8("^(\\d{8})[\\sTt](0?\\d|1\\d|2[0123])(:)(0?[1-9]|[1-5]\\d)\\3(0?[1-9]|[1-5]\\d)$",
1, 2, 4, 5),
// yyyyMMddHHmmss 到 yyyyMMddHHmmssSSSSSSSSS
DATE_TIME_14_TO_23("^\\d{14,23}$"),
// yyyyMMddHHmmss.S 到 yyyyMMddHHmmss.SSSSSSSSS
DATE_TIME_16_TO_23_WITH_DOT("^\\d{14}[.]\\d{1,9}$"),
;
private final Pattern p;
private final int[] groups;
DatetimePattern(String reg, int... groups) {
this.p = Pattern.compile(reg);
this.groups = groups;
}
LocalDateTime parse(String dateStr) {
Matcher m = p.matcher(dateStr);
if (m.find()) {
return switch (this) {
case DATE_TIME, DATE_TIME_CN -> LocalDateTime.of(
Integer.parseInt(m.group(groups[0])),
Integer.parseInt(m.group(groups[1])),
Integer.parseInt(m.group(groups[2])),
Integer.parseInt(m.group(groups[3])),
Integer.parseInt(m.group(groups[4])),
Integer.parseInt(m.group(groups[5]))
);
case DATE_TIME_6 -> LocalDateTime.of(
LocalDate.parse(m.group(groups[0]), DateTimeFormatter.ofPattern("yyMMdd")),
LocalTime.of(Integer.parseInt(m.group(groups[1])),
Integer.parseInt(m.group(groups[2])),
Integer.parseInt(m.group(groups[3])))
);
case DATE_TIME_8 -> LocalDateTime.of(
LocalDate.parse(m.group(groups[0]), DateTimeFormatter.ofPattern("yyyyMMdd")),
LocalTime.of(Integer.parseInt(m.group(groups[1])),
Integer.parseInt(m.group(groups[2])),
Integer.parseInt(m.group(groups[3])))
);
case DATE_TIME_14_TO_23 -> {
int len = dateStr.length();
StringBuilder sb = new StringBuilder(len);
sb.append("yyyyMMddHHmmss");
for (int i = 0, s = len - 14; i < s; i++) {
sb.append("S");
}
yield LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(sb.toString()));
}
case DATE_TIME_16_TO_23_WITH_DOT -> {
int len = dateStr.length();
StringBuilder sb = new StringBuilder(len);
sb.append("yyyyMMddHHmmss.S");
for (int i = 0, s = len - 16; i < s; i++) {
sb.append("S");
}
yield LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(sb.toString()));
}
};
}
return null;
}
}
private static ConstructorConstructor newConstructor() {
return new ConstructorConstructor(
Collections.emptyMap(), // 空的自定义实例创建器
false, // 关闭 JDK Unsafe
Collections.emptyList() // 无反射过滤
);
}
}
三、两个辅助代码
public interface ApiDict<C, T extends Enum<T>> {
C getCode();
String getDesc();
default String[] alternate() {
return null;
}
}
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface IgnoreUnknownEnum {
}


1382

被折叠的 条评论
为什么被折叠?



