diff --git a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java index 0a2f6198..a40caf62 100644 --- a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java +++ b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java @@ -17,6 +17,7 @@ package top.continew.starter.json.jackson.autoconfigure; import cn.hutool.core.date.DatePattern; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -30,8 +31,12 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; +import top.continew.starter.core.enums.BaseEnum; import top.continew.starter.core.util.GeneralPropertySourceFactory; +import top.continew.starter.json.jackson.serializer.BaseEnumDeserializer; +import top.continew.starter.json.jackson.serializer.BaseEnumSerializer; import top.continew.starter.json.jackson.serializer.BigNumberSerializer; +import top.continew.starter.json.jackson.serializer.SimpleDeserializersWrapper; import java.math.BigInteger; import java.time.LocalDate; @@ -54,26 +59,53 @@ public class JacksonAutoConfiguration { @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -> { - // 针对大数值的序列化处理 - JavaTimeModule javaTimeModule = new JavaTimeModule(); - javaTimeModule.addSerializer(Long.class, BigNumberSerializer.SERIALIZER_INSTANCE); - javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.SERIALIZER_INSTANCE); - javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.SERIALIZER_INSTANCE); - // 针对时间类型:LocalDateTime 的序列化和反序列化处理 - DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN); - javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter)); - javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter)); - // 针对时间类型:LocalDate 的序列化和反序列化处理 - DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN); - javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter)); - javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter)); - // 针对时间类型:LocalTime 的序列化和反序列化处理 - DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN); - javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter)); - javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter)); + JavaTimeModule javaTimeModule = this.timeModule(); + SimpleModule simpleModule = this.simpleModule(); builder.timeZone(TimeZone.getDefault()); - builder.modules(javaTimeModule); + builder.modules(javaTimeModule, simpleModule); log.debug("[ContiNew Starter] - Auto Configuration 'Jackson' completed initialization."); }; } + + /** + * 日期时间序列化及反序列化配置 + * + * @return JavaTimeModule / + * @since 1.0.0 + */ + private JavaTimeModule timeModule() { + // 针对大数值的序列化处理 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addSerializer(Long.class, BigNumberSerializer.SERIALIZER_INSTANCE); + javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.SERIALIZER_INSTANCE); + javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.SERIALIZER_INSTANCE); + // 针对时间类型:LocalDateTime 的序列化和反序列化处理 + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN); + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter)); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter)); + // 针对时间类型:LocalDate 的序列化和反序列化处理 + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN); + javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter)); + javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter)); + // 针对时间类型:LocalTime 的序列化和反序列化处理 + DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN); + javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter)); + javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter)); + return javaTimeModule; + } + + /** + * 枚举序列化及反序列化配置 + * + * @return SimpleModule / + * @since 2.4.0 + */ + private SimpleModule simpleModule() { + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(BaseEnum.class, BaseEnumSerializer.SERIALIZER_INSTANCE); + SimpleDeserializersWrapper deserializers = new SimpleDeserializersWrapper(); + deserializers.addDeserializer(BaseEnum.class, BaseEnumDeserializer.SERIALIZER_INSTANCE); + simpleModule.setDeserializers(deserializers); + return simpleModule; + } } diff --git a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumDeserializer.java b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumDeserializer.java new file mode 100644 index 00000000..cb05eead --- /dev/null +++ b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumDeserializer.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.json.jackson.serializer; + +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ReflectUtil; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import top.continew.starter.core.enums.BaseEnum; + +import java.io.IOException; +import java.lang.reflect.Field; + +/** + * 枚举接口 BaseEnum 反序列化器 + * + * @author Charles7c + * @see BaseEnum + * @since 2.4.0 + */ +@JacksonStdImpl +public class BaseEnumDeserializer extends JsonDeserializer { + + /** + * 静态实例 + */ + public static final BaseEnumDeserializer SERIALIZER_INSTANCE = new BaseEnumDeserializer(); + + @Override + public BaseEnum deserialize(JsonParser jsonParser, + DeserializationContext deserializationContext) throws IOException { + Class targetClass = jsonParser.getCurrentValue().getClass(); + String fieldName = jsonParser.getCurrentName(); + String value = jsonParser.getText(); + return this.getEnum(targetClass, value, fieldName); + } + + /** + * 通过某字段对应值获取枚举实例,获取不到时为 {@code null} + * + * @param targetClass 目标类型 + * @param value 字段值 + * @param fieldName 字段名 + * @return 对应枚举实例 ,获取不到时为 {@code null} + */ + private BaseEnum getEnum(Class targetClass, String value, String fieldName) { + Field field = ReflectUtil.getField(targetClass, fieldName); + Class fieldTypeClass = field.getType(); + Object[] enumConstants = fieldTypeClass.getEnumConstants(); + for (Object enumConstant : enumConstants) { + if (ClassUtil.isAssignable(BaseEnum.class, fieldTypeClass)) { + BaseEnum baseEnum = (BaseEnum)enumConstant; + if (baseEnum.getValue().equals(Integer.valueOf(value))) { + return baseEnum; + } + } + } + return null; + } +} diff --git a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumSerializer.java b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumSerializer.java new file mode 100644 index 00000000..dcdcffbb --- /dev/null +++ b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/BaseEnumSerializer.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.json.jackson.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; +import top.continew.starter.core.enums.BaseEnum; + +import java.io.IOException; + +/** + * 枚举接口 BaseEnum 序列化器 + * + * @author Charles7c + * @see BaseEnum + * @since 2.4.0 + */ +@JacksonStdImpl +public class BaseEnumSerializer extends JsonSerializer { + + /** + * 静态实例 + */ + public static final BaseEnumSerializer SERIALIZER_INSTANCE = new BaseEnumSerializer(); + + @Override + public void serialize(BaseEnum value, JsonGenerator generator, SerializerProvider serializers) throws IOException { + generator.writeObject(value.getValue()); + } +} diff --git a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/SimpleDeserializersWrapper.java b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/SimpleDeserializersWrapper.java new file mode 100644 index 00000000..90899384 --- /dev/null +++ b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/continew/starter/json/jackson/serializer/SimpleDeserializersWrapper.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.starter.json.jackson.serializer; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.module.SimpleDeserializers; +import com.fasterxml.jackson.databind.type.ClassKey; + +/** + * 反序列化器包装类(重写 Jackson 反序列化枚举方法,参阅:FasterXML/jackson-databind#2842) + * + *

+ * 默认处理:
+ * 1. Jackson 会先查找指定枚举类型对应的反序列化器(例如:GenderEnum 枚举类型,则是找 GenderEnum 枚举类型的对应反序列化器);
+ * 2. 如果找不到则开始查找 Enum 类型(所有枚举父类)的反序列化器;
+ * 3. 如果都找不到则会采用默认的枚举反序列化器(它仅能根据枚举类型的 name、ordinal 来进行反序列化)。 + *

+ *

+ * 重写增强后:
+ * 1. 同默认 1;
+ * 2. 同默认 2;
+ * 3. 如果也找不到 Enum 类型(所有枚举父类)的反序列化器,开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器);
+ * 4. 同默认 3。 + *

+ * + * @author Charles7c + * @since 2.4.0 + */ +public class SimpleDeserializersWrapper extends SimpleDeserializers { + + @Override + public JsonDeserializer findEnumDeserializer(Class type, + DeserializationConfig config, + BeanDescription beanDesc) throws JsonMappingException { + JsonDeserializer deser = super.findEnumDeserializer(type, config, beanDesc); + if (null != deser) { + return deser; + } + // 重写增强:开始查找指定枚举类型的接口的反序列化器(例如:GenderEnum 枚举类型,则是找它的接口 BaseEnum 的反序列化器) + for (Class typeInterface : type.getInterfaces()) { + deser = this._classMappings.get(new ClassKey(typeInterface)); + if (null != deser) { + return deser; + } + } + return null; + } +}