说明
将Java属性解析映射到Json属性。
这是另一个用于Spring的Maven插件项目的一部分,因为比较独立,所以先把这部分功能拆出来,可以用于其它项目。
代码
JsonType类
java
package com.example.study.constant;
/**
* JSON类型
*/
public class JsonType {
public static String STRING = "String";
public static String NUMBER = "Number";
public static String BOOLEAN = "Boolean";
public static String NULL = "Null";
public static String OBJECT = "Object";
public static String ARRAY = "Array";
/**
* 不支持的类型,如二进制等
*/
public static String UNSUPPORTED = "UNSUPPORTED";
}
JavaType类
java
package com.example.study.constant;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.temporal.Temporal;
import java.util.Collection;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
@Slf4j
public class JavaType {
private static final Set<Class<?>> SIMPLE_TYPES = Set.of(
Boolean.class,
Byte.class,
Character.class,
Double.class,
Float.class,
Integer.class,
Long.class,
Short.class,
Void.class,
Void.TYPE,
URI.class,
URL.class,
UUID.class,
Locale.class,
Pattern.class,
Class.class);
private static final Set<Class<?>> SIMPLE_TYPE_ASSIGNABLE_FROMS = Set.of(
Enum.class,
CharSequence.class,
Number.class,
Date.class,
Temporal.class,
ZoneId.class,
TimeZone.class,
File.class,
Path.class,
Charset.class,
Currency.class,
InetAddress.class,
InputStream.class,
OutputStream.class,
ServletRequest.class,
ServletResponse.class);
public static Map<String, String> JAVA_JSON_TYPE_MAP = new HashMap<>();
static {
JAVA_JSON_TYPE_MAP.put("byte", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("short", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("int", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("long", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("char", JsonType.STRING);
JAVA_JSON_TYPE_MAP.put("boolean", JsonType.BOOLEAN);
JAVA_JSON_TYPE_MAP.put("float", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("double", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Byte", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Short", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Integer", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Long", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Character", JsonType.STRING);
JAVA_JSON_TYPE_MAP.put("java.lang.Boolean", JsonType.BOOLEAN);
JAVA_JSON_TYPE_MAP.put("java.lang.Float", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.Double", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.lang.String", JsonType.STRING);
JAVA_JSON_TYPE_MAP.put("java.math.BigInteger", JsonType.NUMBER);
JAVA_JSON_TYPE_MAP.put("java.math.BigDecimal", JsonType.NUMBER);
}
/**
* 判断是否是基本类型
*
* @param clazz 类class
* @return true=基本类型/false=包装类or对象
*/
public static boolean isBaseType(Class<?> clazz) {
if (clazz == null) {
return false;
}
return clazz.isPrimitive();
}
/**
* 判断是否是简单类型
*
* @param clazz 类class
* @return true=简单类型/false=复杂类型
*/
public static boolean isSimpleType(Class<?> clazz) {
if (clazz == null) {
return false;
}
if (clazz.isPrimitive() || SIMPLE_TYPES.contains(clazz)) {
return true;
}
for (Class<?> assignableFrom : SIMPLE_TYPE_ASSIGNABLE_FROMS) {
if (assignableFrom.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
/**
* 返回java类型对应的json类型
*
* @param clazz 类class
* @return json类型
*/
public static String getJsonType(Class<?> clazz) {
if (clazz == null) {
return JsonType.NULL;
}
if (isMap(clazz)) {
return JsonType.OBJECT;
}
if (isCollection(clazz) || clazz.isArray()) {
return JsonType.ARRAY;
}
String jsonType = JAVA_JSON_TYPE_MAP.get(clazz.getName());
if (jsonType != null) {
return jsonType;
}
if (Number.class.isAssignableFrom(clazz)) {
return JsonType.NUMBER;
}
if (Boolean.class.isAssignableFrom(clazz)) {
return JsonType.BOOLEAN;
}
if (InputStream.class.isAssignableFrom(clazz)
|| OutputStream.class.isAssignableFrom(clazz)
|| ServletRequest.class.isAssignableFrom(clazz)
|| ServletResponse.class.isAssignableFrom(clazz)) {
return JsonType.UNSUPPORTED;
}
return isSimpleType(clazz) ? JsonType.STRING : JsonType.OBJECT;
}
/**
* 判断是否是Map
*
* @param clazz 类class
* @return true=是Map/false=不是Map
*/
public static boolean isMap(Class<?> clazz) {
if (clazz == null) {
return false;
}
return Map.class.isAssignableFrom(clazz);
}
/**
* 判断是否是Collection
*
* @param clazz 类class
* @return true=是Collection/false=不是Collection
*/
public static boolean isCollection(Class<?> clazz) {
if (clazz == null) {
return false;
}
return Collection.class.isAssignableFrom(clazz);
}
}
对象属性类PropertyInfo
java
package com.example.study.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
/**
* 对象属性
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PropertyInfo {
/**
* 变量名称(不考虑三方json库注解的情况下,也是json字段名
*/
private String name;
/**
* java类型
*/
private String javaType;
/**
* json类型
*/
private String jsonType;
/**
* 子属性
*/
private List<PropertyInfo> params;
}
对象属性解析器ObjectParser
java
package com.example.study.util;
import com.example.study.constant.JavaType;
import com.example.study.constant.JsonType;
import com.example.study.entity.PropertyInfo;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 对象属性解析器
*/
public class ObjectParser {
private static final String NO_NAME = "";
/**
* 解析POJO,映射Java属性为JSON属性
*
* @param clazz 类class
* @return Java、JSON属性映射
*/
public static List<PropertyInfo> parseObject(Class<?> clazz) {
if (clazz == null) {
return new ArrayList<>();
}
List<PropertyInfo> list = new ArrayList<>();
Set<Class<?>> parsedPojoSet = new HashSet<>();
for (Field declaredField : clazz.getDeclaredFields()) {
doParseObject(declaredField.getType(), declaredField, list, parsedPojoSet);
}
return list;
}
private static void doParseObject(Class<?> clazz, Field field, List<PropertyInfo> list, Set<Class<?>> parsedPojoSet) {
if (JavaType.isSimpleType(clazz) || JavaType.isMap(clazz) || field == null) {
PropertyInfo info = PropertyInfo.builder()
.name(field == null ? NO_NAME : field.getName())
.javaType(clazz.getTypeName())
.jsonType(JavaType.getJsonType(clazz))
.build();
list.add(info);
return;
}
Type genericType = field.getGenericType();
parseByGenericType(genericType, field.getName(), list, parsedPojoSet);
}
private static void parseByGenericType(Type genericType, String name, List<PropertyInfo> list, Set<Class<?>> parsedPojoSet) {
if (genericType instanceof ParameterizedType parameterizedType) {
// List<List<String>>、List<String[]>
Type type = parameterizedType.getActualTypeArguments()[0];
PropertyInfo info = PropertyInfo.builder()
.name(name)
.javaType(genericType.getTypeName())
.jsonType(JsonType.ARRAY)
.params(new ArrayList<>())
.build();
list.add(info);
if (parameterizedType.getActualTypeArguments().length == 0) {
return;
}
parseByGenericType(type, NO_NAME, info.getParams(), parsedPojoSet);
} else if (genericType instanceof GenericArrayType genericArrayType) {
// 泛型数组 List<T>[] T[]
PropertyInfo info = PropertyInfo.builder()
.name(name)
.javaType(genericType.getTypeName())
.jsonType(JsonType.ARRAY)
.params(new ArrayList<>())
.build();
list.add(info);
parseByGenericType(genericArrayType.getGenericComponentType(), NO_NAME, info.getParams(), parsedPojoSet);
} else if (genericType instanceof WildcardType wildcardType) {
// ? extends Object
Type[] upperBounds = wildcardType.getUpperBounds();
// 虽然到目前为止,通配符最多只能有一个上限,但应该编写此方法以适应多个界限
if (upperBounds.length == 0) {
return;
}
Type upperBound = upperBounds[0];
parseByGenericType(upperBound, name, list, parsedPojoSet);
} else if (genericType instanceof TypeVariable) {
// 泛型,如T K V E等
list.add(PropertyInfo.builder()
.name(NO_NAME)
.javaType(genericType.getTypeName())
.jsonType(JsonType.OBJECT)
.build());
} else if (genericType instanceof Class<?> clazz) {
// 一般类型。数组、对象等
if (clazz.isArray()) {
PropertyInfo info = PropertyInfo.builder()
.name(name)
.javaType(genericType.getTypeName())
.jsonType(JsonType.ARRAY)
.params(new ArrayList<>())
.build();
list.add(info);
parseByGenericType(clazz.getComponentType(), NO_NAME, info.getParams(), parsedPojoSet);
} else if (!JavaType.isSimpleType(clazz)) {
boolean isCollection = JavaType.isCollection(clazz);
boolean alreadyParsed = parsedPojoSet.contains(clazz);
if (alreadyParsed || isCollection) {
String javaType = alreadyParsed ? "$ref:" + genericType.getTypeName() : genericType.getTypeName();
list.add(PropertyInfo.builder()
.name(name)
.javaType(javaType)
.jsonType(isCollection ? JsonType.ARRAY : JsonType.OBJECT)
.build());
return;
}
PropertyInfo info = PropertyInfo.builder()
.name(name)
.javaType(genericType.getTypeName())
.jsonType(JsonType.OBJECT)
.params(new ArrayList<>())
.build();
list.add(info);
if (isPojo(clazz)) {
parsedPojoSet.add(clazz);
}
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.getName().startsWith("this$")) {
continue;
}
doParseObject(declaredField.getType(), declaredField, info.getParams(), parsedPojoSet);
}
} else {
doParseObject(clazz, null, list, parsedPojoSet);
}
}
}
private static boolean isPojo(Class<?> type) {
return !(JavaType.isSimpleType(type)
|| Object.class.equals(type)
|| JavaType.isCollection(type)
|| JavaType.isMap(type));
}
}