init: 导入RuoYi‑Vue‑Plus 6.X完整代码

This commit is contained in:
admin
2026-08-09 17:51:11 +08:00
commit 51cac459b3
914 changed files with 103833 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-json</artifactId>
<description>
ruoyi-common-json 序列化模块
</description>
<dependencies>
<!-- 核心模块 -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<!-- Jackson 序列化 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jackson</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,64 @@
package org.dromara.common.json.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.handler.BigNumberSerializer;
import org.dromara.common.json.handler.CustomDateDeserializer;
import org.dromara.common.json.handler.CustomLocalDateTimeDeserializer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.ser.std.ToStringSerializer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
/**
* jackson 配置
*
* @author Lion Li
*/
@Slf4j
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
/**
* 注册 Jackson 序列化与反序列化模块。
*
* @return Jackson 模块
*/
@Bean
public SimpleModule registerJavaTimeModule() {
// 全局配置序列化返回 JSON 处理
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
module.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
module.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
module.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
module.addDeserializer(Date.class, new CustomDateDeserializer());
return module;
}
/**
* 初始化 Jackson 构建器默认配置。
*
* @return Jackson 构建器自定义器
*/
@Bean
public JsonMapperBuilderCustomizer jsonInitCustomizer() {
return builder -> {
builder.defaultTimeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置");
};
}
}
@@ -0,0 +1,29 @@
package org.dromara.common.json.config;
import org.dromara.common.json.enhance.JsonFieldProcessor;
import org.dromara.common.json.enhance.JsonValueEnhancer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import tools.jackson.databind.json.JsonMapper;
import java.util.List;
/**
* 响应增强核心配置。
*/
@AutoConfiguration
public class JsonEnhancementConfig {
/**
* 创建 JSON 字段增强处理器入口。
*
* @param jsonMapper JSON 映射器
* @param processors 字段处理器集合
* @return JSON 值增强器
*/
@Bean
public JsonValueEnhancer jsonValueEnhancer(JsonMapper jsonMapper, List<JsonFieldProcessor> processors) {
return new JsonValueEnhancer(jsonMapper, processors);
}
}
@@ -0,0 +1,88 @@
package org.dromara.common.json.enhance;
import lombok.Getter;
import tools.jackson.databind.json.JsonMapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
/**
* 单次响应增强上下文。
*/
@Getter
public class JsonEnhancementContext {
private final JsonMapper jsonMapper;
private final Map<String, Object> attributes = new LinkedHashMap<>();
private boolean processingRequired;
/**
* 构造响应增强上下文。
*
* @param jsonMapper JSON 映射器
*/
public JsonEnhancementContext(JsonMapper jsonMapper) {
this.jsonMapper = jsonMapper;
}
/**
* 获取上下文属性。
*
* @param key 属性键
* @param <T> 属性值类型
* @return 属性值
*/
@SuppressWarnings("unchecked")
public <T> T getAttribute(String key) {
return (T) attributes.get(key);
}
/**
* 获取上下文属性,不存在时创建并写入。
*
* @param key 属性键
* @param supplier 属性值创建器
* @param <T> 属性值类型
* @return 属性值
*/
@SuppressWarnings("unchecked")
public <T> T getOrCreateAttribute(String key, Supplier<T> supplier) {
Object value = attributes.computeIfAbsent(key, ignored -> supplier.get());
return (T) value;
}
/**
* 设置上下文属性。
*
* @param key 属性键
* @param value 属性值
*/
public void setAttribute(String key, Object value) {
attributes.put(key, value);
}
/**
* 判断上下文是否包含指定属性。
*/
public boolean containsAttribute(String key) {
return attributes.containsKey(key);
}
/**
* 移除上下文属性。
*/
public void removeAttribute(String key) {
attributes.remove(key);
}
/**
* 标记本次响应存在需要处理的字段。
*/
public void markProcessingRequired() {
this.processingRequired = true;
}
}
@@ -0,0 +1,23 @@
package org.dromara.common.json.enhance;
import tools.jackson.databind.introspect.AnnotatedMember;
import java.lang.annotation.Annotation;
/**
* 响应字段上下文。
*/
public record JsonFieldContext(Object owner, String propertyName, AnnotatedMember member, Object value) {
/**
* 获取字段上的指定注解。
*
* @param annotationType 注解类型
* @param <A> 注解类型
* @return 注解对象
*/
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
return member == null ? null : member.getAnnotation(annotationType);
}
}
@@ -0,0 +1,53 @@
package org.dromara.common.json.enhance;
/**
* 响应字段处理器。
*
* <p>生命周期按顺序分为三个阶段,由 {@link JsonValueEnhancer} 统一驱动:
* <ol>
* <li><b>collect</b>(收集阶段):递归扫描响应对象树时,对每个字段调用一次。
* 用于采集需要处理的字段 key,存入 {@link JsonEnhancementContext} 供下一阶段批量处理。</li>
* <li><b>prepare</b>(预处理阶段):collect 全部完成后调用一次。
* 用于执行批量 IO(如批量查询数据库),将结果写入 {@link JsonEnhancementContext}。
* 此阶段应消除 N+1 查询问题。</li>
* <li><b>process</b>(处理阶段):渲染响应 JSON 树时,对每个字段调用一次。
* 从 {@link JsonEnhancementContext} 取出 prepare 阶段写入的结果,返回替换后的字段值。
* 返回原 {@code value} 表示不修改;返回 {@code null} 表示将字段值置为 null。</li>
* </ol>
*
* <p>实现类通过 {@link JsonEnhancementContext#setAttribute} / {@link JsonEnhancementContext#getAttribute}
* 在三个阶段之间共享数据,建议以实现类全限定名作为 attribute key 前缀以避免冲突。
*/
public interface JsonFieldProcessor {
/**
* 判断当前处理器是否需要处理该字段。
* 默认返回 true 以兼容无注解驱动的自定义处理器。
*/
default boolean supports(JsonFieldContext fieldContext) {
return true;
}
/**
* 收集阶段:扫描字段,将需要处理的 key 存入 context。
* 每个字段调用一次,整个对象树扫描完成后才会进入 prepare 阶段。
*/
default void collect(JsonFieldContext fieldContext, JsonEnhancementContext context) {
}
/**
* 预处理阶段:基于 collect 阶段收集到的数据执行批量处理(如批量查询),结果写入 context。
* 每次响应只调用一次。
*/
default void prepare(JsonEnhancementContext context) {
}
/**
* 处理阶段:根据 prepare 阶段写入的结果,对字段值进行替换并返回。
* 返回原 {@code value} 表示不修改该字段;返回 {@code null} 表示将字段值置为 null。
*/
default Object process(JsonFieldContext fieldContext, Object value, JsonEnhancementContext context) {
return value;
}
}
@@ -0,0 +1,377 @@
package org.dromara.common.json.enhance;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.SerializationConfig;
import tools.jackson.databind.introspect.AnnotatedClass;
import tools.jackson.databind.introspect.AnnotatedMember;
import tools.jackson.databind.introspect.BeanPropertyDefinition;
import tools.jackson.databind.introspect.ClassIntrospector;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
import java.lang.reflect.Array;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 统一响应增强器,支持在出站前执行翻译、脱敏等字段处理。
*/
public class JsonValueEnhancer {
/**
* JSON 映射器。
*/
private final JsonMapper jsonMapper;
/**
* 字段增强处理器列表。
*/
private final List<JsonFieldProcessor> processors;
/**
* 类型属性元数据缓存。
*/
private final Map<Class<?>, List<PropertyMetadata>> propertyCache = new ConcurrentHashMap<>();
/**
* 构造统一响应增强器。
*
* @param jsonMapper JSON 映射器
* @param processors 字段处理器列表
*/
public JsonValueEnhancer(JsonMapper jsonMapper, List<JsonFieldProcessor> processors) {
this.jsonMapper = jsonMapper;
List<JsonFieldProcessor> sortedProcessors = new ArrayList<>(processors);
AnnotationAwareOrderComparator.sort(sortedProcessors);
this.processors = Collections.unmodifiableList(sortedProcessors);
}
/**
* 增强响应对象。
*
* @param body 响应对象
* @return 增强后的响应对象
*/
public Object enhance(Object body) {
if (body == null || body instanceof JsonNode || processors.isEmpty()) {
return body;
}
JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper);
collectValue(body, context, new IdentityHashMap<>());
if (!context.isProcessingRequired()) {
return body;
}
processors.forEach(processor -> processor.prepare(context));
return renderValue(body, context, new IdentityHashMap<>());
}
/**
* 判断消息转换器是否支持响应增强。
*
* @param converterType 消息转换器类型
* @return true 支持 false 不支持
*/
public boolean supports(Class<?> converterType) {
return !processors.isEmpty()
&& !ByteArrayHttpMessageConverter.class.isAssignableFrom(converterType)
&& !StringHttpMessageConverter.class.isAssignableFrom(converterType)
&& !ResourceHttpMessageConverter.class.isAssignableFrom(converterType);
}
/**
* 对已处理后的对象再次执行树形增强。
*
* @param value 待增强对象
* @return 增强后的 JSON 节点
*/
private JsonNode enhanceTree(Object value) {
JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper);
collectValue(value, context, new IdentityHashMap<>());
if (!context.isProcessingRequired()) {
return jsonMapper.valueToTree(value);
}
processors.forEach(processor -> processor.prepare(context));
return renderValue(value, context, new IdentityHashMap<>());
}
/**
* 递归收集对象中需要增强的字段信息。
*
* @param value 当前对象
* @param context 增强上下文
* @param visited 已访问对象集合,用于避免循环引用
*/
private void collectValue(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
switch (value) {
case null -> {
return;
}
case Map<?, ?> map -> {
map.values().forEach(child -> collectValue(child, context, visited));
return;
}
case Iterable<?> iterable -> {
iterable.forEach(child -> collectValue(child, context, visited));
return;
}
default -> {
}
}
if (value.getClass().isArray()) {
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
collectValue(Array.get(value, i), context, visited);
}
return;
}
if (isSimpleValue(value.getClass()) || visited.put(value, Boolean.TRUE) != null) {
return;
}
try {
for (PropertyMetadata metadata : getProperties(value.getClass())) {
Object propertyValue = metadata.getValue(value);
JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), propertyValue);
collectField(fieldContext, context);
collectValue(propertyValue, context, visited);
}
} finally {
visited.remove(value);
}
}
/**
* 收集单个字段的增强信息。
*
* @param fieldContext 字段上下文
* @param context 增强上下文
*/
private void collectField(JsonFieldContext fieldContext, JsonEnhancementContext context) {
for (JsonFieldProcessor processor : processors) {
if (processor.supports(fieldContext)) {
context.markProcessingRequired();
processor.collect(fieldContext, context);
}
}
}
/**
* 将对象渲染为增强后的 JSON 节点。
*
* @param value 当前对象
* @param context 增强上下文
* @param visited 已访问对象集合,用于避免循环引用
* @return JSON 节点
*/
private JsonNode renderValue(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
switch (value) {
case null -> {
return jsonMapper.nullNode();
}
case JsonNode jsonNode -> {
return jsonNode;
}
case Map<?, ?> map -> {
return renderMap(map, context, visited);
}
case Iterable<?> iterable -> {
return renderIterable(iterable, context, visited);
}
default -> {
}
}
if (value.getClass().isArray()) {
return renderArray(value, context, visited);
}
if (isSimpleValue(value.getClass())) {
return jsonMapper.valueToTree(value);
}
if (visited.put(value, Boolean.TRUE) != null) {
return jsonMapper.valueToTree(value);
}
try {
return renderPojo(value, context, visited);
} finally {
visited.remove(value);
}
}
/**
* 渲染 Map 对象。
*
* @param map Map 对象
* @param context 增强上下文
* @param visited 已访问对象集合
* @return 对象节点
*/
private ObjectNode renderMap(Map<?, ?> map, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
ObjectNode objectNode = jsonMapper.createObjectNode();
map.forEach((key, childValue) -> objectNode.set(String.valueOf(key), renderValue(childValue, context, visited)));
return objectNode;
}
/**
* 渲染可迭代对象。
*
* @param iterable 可迭代对象
* @param context 增强上下文
* @param visited 已访问对象集合
* @return 数组节点
*/
private ArrayNode renderIterable(Iterable<?> iterable, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
ArrayNode arrayNode = jsonMapper.createArrayNode();
for (Object child : iterable) {
arrayNode.add(renderValue(child, context, visited));
}
return arrayNode;
}
/**
* 渲染数组对象。
*
* @param value 数组对象
* @param context 增强上下文
* @param visited 已访问对象集合
* @return 数组节点
*/
private ArrayNode renderArray(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
ArrayNode arrayNode = jsonMapper.createArrayNode();
int length = Array.getLength(value);
for (int i = 0; i < length; i++) {
arrayNode.add(renderValue(Array.get(value, i), context, visited));
}
return arrayNode;
}
/**
* 渲染普通 Java 对象。
*
* @param value Java 对象
* @param context 增强上下文
* @param visited 已访问对象集合
* @return 对象节点
*/
private ObjectNode renderPojo(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
ObjectNode objectNode = jsonMapper.createObjectNode();
for (PropertyMetadata metadata : getProperties(value.getClass())) {
Object originalValue = metadata.getValue(value);
JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), originalValue);
Object processedValue = originalValue;
boolean changed = false;
for (JsonFieldProcessor processor : processors) {
if (!processor.supports(fieldContext)) {
continue;
}
Object nextValue = processor.process(fieldContext, processedValue, context);
changed = changed || !Objects.equals(processedValue, nextValue);
processedValue = nextValue;
}
JsonNode childNode = changed
? enhanceTranslatedValue(processedValue, context, visited)
: renderValue(processedValue, context, visited);
objectNode.set(metadata.propertyName(), childNode);
}
return objectNode;
}
/**
* 对字段处理后得到的复杂对象执行二次增强。
*
* @param value 字段处理后的值
* @param context 增强上下文
* @param visited 已访问对象集合
* @return JSON 节点
*/
private JsonNode enhanceTranslatedValue(Object value, JsonEnhancementContext context, IdentityHashMap<Object, Boolean> visited) {
if (value == null || value instanceof JsonNode || isSimpleValue(value.getClass())) {
return renderValue(value, context, visited);
}
return enhanceTree(value);
}
/**
* 获取指定类型可序列化属性元数据。
*
* @param type 类型
* @return 属性元数据列表
*/
private List<PropertyMetadata> getProperties(Class<?> type) {
return propertyCache.computeIfAbsent(type, this::resolveProperties);
}
/**
* 解析指定类型可序列化属性元数据。
*
* @param type 类型
* @return 属性元数据列表
*/
private List<PropertyMetadata> resolveProperties(Class<?> type) {
if (isSimpleValue(type) || type.isArray() || Map.class.isAssignableFrom(type) || Iterable.class.isAssignableFrom(type)) {
return Collections.emptyList();
}
JavaType javaType = jsonMapper.constructType(type);
SerializationConfig config = jsonMapper.serializationConfig();
ClassIntrospector classIntrospector = config.classIntrospectorInstance().forOperation(config);
AnnotatedClass annotatedClass = classIntrospector.introspectClassAnnotations(javaType);
List<BeanPropertyDefinition> definitions = classIntrospector.introspectForSerialization(javaType, annotatedClass).findProperties();
List<PropertyMetadata> properties = new ArrayList<>(definitions.size());
for (BeanPropertyDefinition definition : definitions) {
AnnotatedMember member = definition.getAccessor();
if (member == null) {
member = definition.getField();
}
if (member == null) {
continue;
}
member.fixAccess(true);
properties.add(new PropertyMetadata(definition.getName(), member));
}
return Collections.unmodifiableList(properties);
}
/**
* 判断类型是否为简单值类型。
*
* @param type 类型
* @return true 简单值 false 复杂对象
*/
private boolean isSimpleValue(Class<?> type) {
return type.isPrimitive()
|| CharSequence.class.isAssignableFrom(type)
|| Number.class.isAssignableFrom(type)
|| Boolean.class == type
|| Character.class == type
|| Date.class.isAssignableFrom(type)
|| Temporal.class.isAssignableFrom(type)
|| Enum.class.isAssignableFrom(type)
|| UUID.class.isAssignableFrom(type)
|| Class.class == type;
}
/**
* JSON 属性元数据。
*
* @param propertyName 属性名称
* @param member Jackson 属性成员
*/
private record PropertyMetadata(String propertyName, AnnotatedMember member) {
/**
* 从源对象读取属性值。
*
* @param source 源对象
* @return 属性值
*/
Object getValue(Object source) {
return member.getValue(source);
}
}
}
@@ -0,0 +1,54 @@
package org.dromara.common.json.handler;
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.annotation.JacksonStdImpl;
import tools.jackson.databind.ser.jdk.NumberSerializer;
/**
* 超出 JS 最大最小值 处理
*
* @author Lion Li
*/
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
/**
* 提供实例
*/
public static final BigNumberSerializer INSTANCE = new BigNumberSerializer(Number.class);
/**
* 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来
*/
private static final long MAX_SAFE_INTEGER = 9007199254740991L;
/**
* JavaScript 最小安全整数。
*/
private static final long MIN_SAFE_INTEGER = -9007199254740991L;
/**
* 构造大数字序列化器。
*
* @param rawType 数字类型
*/
public BigNumberSerializer(Class<? extends Number> rawType) {
super(rawType);
}
/**
* 序列化数字,超出 JS 安全整数范围时输出字符串。
*
* @param value 数字值
* @param gen JSON 生成器
* @param provider 序列化上下文
*/
@Override
public void serialize(Number value, JsonGenerator gen, SerializationContext provider) {
// 超出范围 序列化为字符串
if (value.longValue() >= MIN_SAFE_INTEGER && value.longValue() <= MAX_SAFE_INTEGER) {
super.serialize(value, gen, provider);
} else {
gen.writeString(value.toString());
}
}
}
@@ -0,0 +1,35 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.ValueDeserializer;
import java.util.Date;
/**
* 自定义 Date 类型反序列化处理器(支持多种格式)
*
* @author AprilWind
*/
public class CustomDateDeserializer extends ValueDeserializer<Date> {
/**
* 反序列化逻辑:将字符串转换为 Date 对象
*
* @param p JSON 解析器,用于获取字符串值
* @param ctxt 上下文环境(可用于获取更多配置)
* @return 转换后的 Date 对象,若为空字符串返回 null
*/
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) {
String text = p.getString();
if (text == null || text.isBlank()) {
return null;
}
DateTime parse = DateUtil.parse(text.trim());
return parse.toJdkDate();
}
}
@@ -0,0 +1,35 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.ValueDeserializer;
import java.time.LocalDateTime;
/**
* 自定义 LocalDateTime 类型反序列化处理器
*
* @author AprilWind
*/
public class CustomLocalDateTimeDeserializer extends ValueDeserializer<LocalDateTime> {
/**
* 反序列化逻辑:将字符串转换为 LocalDateTime 对象
*
* @param p JSON 解析器,用于获取字符串值
* @param ctxt 上下文环境(可用于获取更多配置)
* @return 转换后的 LocalDateTime 对象,若为空字符串返回 null
*/
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) {
String text = p.getString();
if (text == null || text.isBlank()) {
return null;
}
DateTime parse = DateUtil.parse(text.trim());
return parse.toLocalDateTime();
}
}
@@ -0,0 +1,239 @@
package org.dromara.common.json.utils;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
/**
* 全局 JSON 映射器。
*/
private static final JsonMapper JSON_MAPPER = SpringUtils.getBean(JsonMapper.class);
/**
* 获取全局 JsonMapper 实例。
*
* @return JsonMapper
*/
public static JsonMapper getJsonMapper() {
return JSON_MAPPER;
}
/**
* 将对象转换为JSON格式的字符串
*
* @param object 要转换的对象
* @return JSON格式的字符串,如果对象为null,则返回null
* @throws RuntimeException 如果转换过程中发生JSON处理异常,则抛出运行时异常
*/
public static String toJsonString(Object object) {
if (ObjectUtil.isNull(object)) {
return null;
}
return JSON_MAPPER.writeValueAsString(object);
}
/**
* 将JSON格式的字符串转换为指定类型的对象
*
* @param text JSON格式的字符串
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return null;
}
return JSON_MAPPER.readValue(text, clazz);
}
/**
* 将字节数组转换为指定类型的对象
*
* @param bytes 字节数组
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字节数组为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
return JSON_MAPPER.readValue(bytes, clazz);
}
/**
* 将JSON格式的字符串转换为指定类型的对象,支持复杂类型
*
* @param text JSON格式的字符串
* @param typeReference 指定类型的TypeReference对象
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StringUtils.isBlank(text)) {
return null;
}
return JSON_MAPPER.readValue(text, typeReference);
}
/**
* 将JSON格式的字符串转换为Dict对象
*
* @param text JSON格式的字符串
* @return 转换后的Dict对象,如果字符串为空或者不是JSON格式则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static Dict parseMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
return JSON_MAPPER.readValue(text, JSON_MAPPER.getTypeFactory().constructType(Dict.class));
}
/**
* 将JSON格式的字符串转换为Dict对象的列表
*
* @param text JSON格式的字符串
* @return 转换后的Dict对象的列表,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static List<Dict> parseArrayMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
return JSON_MAPPER.readValue(text, JSON_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
}
/**
* 将JSON格式的字符串转换为指定类型对象的列表
*
* @param text JSON格式的字符串
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象的列表,如果字符串为空则返回空列表
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return new ArrayList<>();
}
return JSON_MAPPER.readValue(text, JSON_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
}
/**
* 将对象转换为 JSON 字符串,并递归移除指定字段。
*
* @param object 要转换的对象
* @param fieldNames 需要移除的字段名
* @return 移除字段后的 JSON 字符串
*/
public static String toJsonStringExcludeFields(Object object, String... fieldNames) {
if (ObjectUtil.isNull(object)) {
return null;
}
JsonNode node = JSON_MAPPER.valueToTree(object);
removeFields(node, fieldNames);
return toJsonString(node);
}
/**
* 从 JSON 树中递归移除指定字段。
*
* @param node JSON 节点
* @param fieldNames 需要移除的字段名
* @return 原 JSON 节点
*/
public static JsonNode removeFields(JsonNode node, String... fieldNames) {
if (node == null || ArrayUtil.isEmpty(fieldNames)) {
return node;
}
if (node.isObject()) {
ObjectNode objectNode = (ObjectNode) node;
for (String fieldName : fieldNames) {
if (StringUtils.isNotBlank(fieldName)) {
objectNode.remove(fieldName);
}
}
}
for (JsonNode child : node) {
removeFields(child, fieldNames);
}
return node;
}
/**
* 判断字符串是否为合法 JSON(对象或数组)
*
* @param str 待校验字符串
* @return true = 合法 JSONfalse = 非法或空
*/
public static boolean isJson(String str) {
return readTreeQuietly(str) != null;
}
/**
* 判断字符串是否为 JSON 对象({})
*
* @param str 待校验字符串
* @return true = JSON 对象
*/
public static boolean isJsonObject(String str) {
JsonNode node = readTreeQuietly(str);
return node != null && node.isObject();
}
/**
* 判断字符串是否为 JSON 数组([])
*
* @param str 待校验字符串
* @return true = JSON 数组
*/
public static boolean isJsonArray(String str) {
if (StringUtils.isBlank(str)) {
return false;
}
JsonNode node = readTreeQuietly(str);
return node != null && node.isArray();
}
/**
* 安静读取 JSON 树,解析失败时返回 null。
*
* @param str JSON 字符串
* @return JSON 节点,解析失败或空字符串时返回 null
*/
private static JsonNode readTreeQuietly(String str) {
if (StringUtils.isBlank(str)) {
return null;
}
try {
return JSON_MAPPER.readTree(str);
} catch (Exception e) {
return null;
}
}
}
@@ -0,0 +1,39 @@
package org.dromara.common.json.validate;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
/**
* JSON 格式校验注解
*
* @author AprilWind
*/
@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = JsonPatternValidator.class)
public @interface JsonPattern {
/**
* 限制 JSON 类型,默认为 {@link JsonType#ANY},即对象或数组都允许
*/
JsonType type() default JsonType.ANY;
/**
* 校验失败时的提示消息
*/
String message() default "不是有效的 JSON 格式";
/**
* Bean Validation 分组。
*/
Class<?>[] groups() default {};
/**
* Bean Validation 负载信息。
*/
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,51 @@
package org.dromara.common.json.validate;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
* JSON 格式校验器
*
* @author AprilWind
*/
public class JsonPatternValidator implements ConstraintValidator<JsonPattern, String> {
/**
* 注解中指定的 JSON 类型枚举
*/
private JsonType jsonType;
/**
* 初始化校验器,从注解中提取 JSON 类型
*
* @param annotation 注解实例
*/
@Override
public void initialize(JsonPattern annotation) {
this.jsonType = annotation.type();
}
/**
* 校验字符串是否为合法 JSON
*
* @param value 待校验字符串
* @param context 校验上下文,可用于自定义错误信息
* @return true = 合法 JSON 或为空,false = 非法 JSON
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isBlank(value)) {
// 交给 @NotBlank 或 @NotNull 控制是否允许为空
return true;
}
// 根据 JSON 类型进行不同的校验
return switch (jsonType) {
case ANY -> JsonUtils.isJson(value);
case OBJECT -> JsonUtils.isJsonObject(value);
case ARRAY -> JsonUtils.isJsonArray(value);
};
}
}
@@ -0,0 +1,30 @@
package org.dromara.common.json.validate;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* JSON 类型枚举
*
* @author AprilWind
*/
@Getter
@AllArgsConstructor
public enum JsonType {
/**
* JSON 对象,例如 {"a":1}
*/
OBJECT,
/**
* JSON 数组,例如 [1,2,3]
*/
ARRAY,
/**
* 任意 JSON 类型,对象或数组都可以
*/
ANY
}
@@ -0,0 +1,2 @@
org.dromara.common.json.config.JacksonConfig
org.dromara.common.json.config.JsonEnhancementConfig