001/* 002 * Copyright (c) 2023-2026, Agents-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.agentsflex.core.util; 017 018import org.jetbrains.annotations.NotNull; 019import org.jetbrains.annotations.Nullable; 020 021import java.lang.reflect.*; 022import java.math.BigDecimal; 023import java.math.BigInteger; 024import java.net.URI; 025import java.net.URL; 026import java.time.*; 027import java.util.*; 028import java.util.concurrent.ConcurrentHashMap; 029 030/** 031 * Java 类型到 JSON Schema 类型的映射工具类 032 * <p> 033 * 支持标准类型、集合类型、泛型类型及自定义类型注册 034 * <p> 035 * JSON Schema 标准类型: string, number, integer, boolean, object, array, null 036 * 037 * @author fuhai 038 * @since 2026/03/10 039 */ 040public final class JsonSchemaTypeMapper { 041 042 /** 043 * 类型映射缓存:避免重复反射解析,提升性能 044 */ 045 private static final Map<Class<?>, String> TYPE_CACHE = new ConcurrentHashMap<>(64); 046 047 /** 048 * 自定义类型映射策略(可扩展) 049 */ 050 private static final Map<Class<?>, TypeMappingStrategy> CUSTOM_STRATEGIES = new ConcurrentHashMap<>(8); 051 052 /** 053 * 私有构造,防止实例化 054 */ 055 private JsonSchemaTypeMapper() { 056 } 057 058 /** 059 * 将 Java Class 映射为 JSON Schema 类型字符串 060 * 061 * @param javaType Java 类型 062 * @return JSON Schema 类型: string/number/integer/boolean/object/array/null 063 */ 064 @NotNull 065 public static String mapToSchemaType(@Nullable Class<?> javaType) { 066 if (javaType == null) { 067 return "string"; 068 } 069 070 // 1. 优先检查自定义策略 071 TypeMappingStrategy custom = CUSTOM_STRATEGIES.get(javaType); 072 if (custom != null) { 073 return custom.mapType(javaType); 074 } 075 076 // 2. 缓存命中 077 return TYPE_CACHE.computeIfAbsent(javaType, JsonSchemaTypeMapper::doMapType); 078 } 079 080 /** 081 * 解析数组/集合元素的 JSON Schema 类型(支持泛型) 082 * 083 * @param genericType 泛型类型信息,如 {@code List<String>} 的 {@code String} 084 * @return 元素类型的 JSON Schema 类型 085 */ 086 @NotNull 087 public static String resolveArrayItemType(@Nullable Type genericType) { 088 if (genericType == null) { 089 return "string"; 090 } 091 092 // 1. ParameterizedType: List<String>, Map<K,V> 093 if (genericType instanceof ParameterizedType) { 094 Type[] args = ((ParameterizedType) genericType).getActualTypeArguments(); 095 if (args.length > 0) { 096 return resolveType(args[0]); 097 } 098 } 099 100 // 2. GenericArrayType: T[], List<T>[] 101 if (genericType instanceof GenericArrayType) { 102 Type componentType = ((GenericArrayType) genericType).getGenericComponentType(); 103 return resolveType(componentType); 104 } 105 106 // 3. WildcardType: List<? extends Number> → 取上界 107 if (genericType instanceof WildcardType) { 108 Type[] upperBounds = ((WildcardType) genericType).getUpperBounds(); 109 if (upperBounds.length > 0) { 110 return resolveType(upperBounds[0]); 111 } 112 Type[] lowerBounds = ((WildcardType) genericType).getLowerBounds(); 113 if (lowerBounds.length > 0) { 114 return resolveType(lowerBounds[0]); 115 } 116 return "object"; 117 } 118 119 // 4. TypeVariable: List<T> → 默认 object(可从上下文进一步解析) 120 if (genericType instanceof TypeVariable) { 121 return "object"; 122 } 123 124 // 5. 普通 Class 125 if (genericType instanceof Class) { 126 return mapToSchemaType((Class<?>) genericType); 127 } 128 129 // 6. 兜底:最宽兼容策略 130 return "object"; 131 } 132 133 /** 134 * 注册自定义类型映射策略 135 * <p> 136 * 示例:{@code registerStrategy(LocalDateTime.class, t -> "string", "date-time")} 137 * 138 * @param type Java 类型 139 * @param strategy 映射策略 140 */ 141 public static void registerStrategy(@NotNull Class<?> type, @NotNull TypeMappingStrategy strategy) { 142 CUSTOM_STRATEGIES.put(type, strategy); 143 TYPE_CACHE.remove(type); // 清除缓存,确保新策略生效 144 } 145 146 /** 147 * 清除缓存(测试或热更新场景使用) 148 */ 149 public static void clearCache() { 150 TYPE_CACHE.clear(); 151 } 152 153 // =============== 内部映射逻辑 =============== 154 155 @NotNull 156 private static String doMapType(@NotNull Class<?> type) { 157 // 1. 数组类型(含多维) 158 if (type.isArray()) { 159 return "array"; 160 } 161 162 // 2. Optional 类型:解包或返回 string(根据业务需求调整) 163 if (Optional.class.isAssignableFrom(type)) { 164 return "string"; 165 } 166 167 // 3. Map → object 168 if (Map.class.isAssignableFrom(type)) { 169 return "object"; 170 } 171 172 // 4. Collection → array 173 if (Collection.class.isAssignableFrom(type)) { 174 return "array"; 175 } 176 177 // 5. 整数类型(使用类型层次判断,避免字符串匹配) 178 if (isIntegerNumericType(type)) { 179 return "integer"; 180 } 181 182 // 6. 浮点类型 183 if (isFloatingNumericType(type)) { 184 return "number"; 185 } 186 187 // 7. 布尔类型 188 if (boolean.class == type || Boolean.class == type) { 189 return "boolean"; 190 } 191 192 // 8. 字符串及类字符串类型 193 if (isStringLikeType(type)) { 194 return "string"; 195 } 196 197 // 9. 枚举类型 198 if (type.isEnum()) { 199 return "string"; 200 } 201 202 // 10. 未知类型(实体类/自定义类):默认 object(符合 JSON Schema 规范) 203 // 大模型传参是 JSON 对象,Java 实体类应对应 object 类型 204 return "object"; 205 } 206 207 @NotNull 208 private static String resolveType(@Nullable Type type) { 209 if (type instanceof Class) { 210 return mapToSchemaType((Class<?>) type); 211 } else if (type instanceof ParameterizedType) { 212 Class<?> rawType = (Class<?>) ((ParameterizedType) type).getRawType(); 213 return mapToSchemaType(rawType); 214 } else { 215 // 递归处理其他 Type 子类型 216 return resolveArrayItemType(type); 217 } 218 } 219 220 // =============== 类型判断辅助方法 =============== 221 222 private static boolean isIntegerNumericType(Class<?> type) { 223 return type == int.class || type == Integer.class || 224 type == long.class || type == Long.class || 225 type == short.class || type == Short.class || 226 type == byte.class || type == Byte.class || 227 type == BigInteger.class; 228 } 229 230 private static boolean isFloatingNumericType(Class<?> type) { 231 return type == float.class || type == Float.class || 232 type == double.class || type == Double.class || 233 type == BigDecimal.class; 234 } 235 236 private static boolean isStringLikeType(Class<?> type) { 237 return type == String.class || 238 type == char.class || type == Character.class || 239 type == CharSequence.class || 240 // 时间类型(大模型通常以字符串传输) 241 type == Date.class || 242 type == java.sql.Date.class || 243 type == java.sql.Timestamp.class || 244 type == LocalDate.class || 245 type == LocalDateTime.class || 246 type == LocalTime.class || 247 type == Instant.class || 248 type == ZonedDateTime.class || 249 type == OffsetDateTime.class || 250 // 其他常见字符串语义类型 251 type == UUID.class || 252 type == URI.class || 253 type == URL.class; 254 } 255 256 /** 257 * 类型映射策略函数式接口(支持扩展) 258 */ 259 @FunctionalInterface 260 public interface TypeMappingStrategy { 261 /** 262 * 将 Java 类型映射为 JSON Schema 类型 263 * 264 * @param javaType Java 类型 265 * @return JSON Schema 类型字符串 266 */ 267 @NotNull 268 String mapType(@NotNull Class<?> javaType); 269 } 270}