Json转为Bean的原理
以下源码来自于hutool工具依赖
一、类型准备
准备俩个类,如下
@Data
class Option<T> {
String optionName;
T value;
}
@Data
class Value<T> {
T value;
}
构建一个泛型
String json = "{'optionName': 'log', 'value': {'value': 'slf4j'}}";
指定类型引用
Option<Value<String>> option = parse.toBean(new TypeReference<Option<Value<String>>>() {});
二、测试结果
@Test
public void test() throws ClassNotFoundException {
String json = "{'optionName': 'log', 'value': {'value': 'slf4j'}}";
JSON parse = JSONUtil.parse(json);
Option<Value<String>> option = parse.toBean(new TypeReference<Option<Value<String>>>() {});
System.out.println(option);
}
三、转换过程分析
源码:
default <T> T toBean(TypeReference<T> reference) {
return toBean(reference.getType());
}
1.类型获取
由TypeReference
的getType
可以获取到一个参数化的类型实现ParamerteredTypeImpl
类的结构大致如下:
public class ParameterizedTypeImpl implements ParameterizedType, Serializable {
private static final long serialVersionUID = 1L;
private final Type[] actualTypeArguments;
private final Type ownerType;
private final Type rawType;
/**
* 构造
*
* @param actualTypeArguments 实际的泛型参数类型
* @param ownerType 拥有者类型
* @param rawType 原始类型
*/
public ParameterizedTypeImpl(Type[] actualTypeArguments, Type ownerType, Type rawType) {
// 泛型列表,比如List<E>列表就只有一个元素,Map<K, V>列表就有两个元素,以此类推
this.actualTypeArguments = actualTypeArguments;
// 明确的持有者,如果当前类型是个内部类,则此类型表示外部持有这个内部类的类型
this.ownerType = ownerType;
// 当前类的原生类型,class
this.rawType = rawType;
}
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}
@Override
public Type getOwnerType() {
return ownerType;
}
@Override
public Type getRawType() {
return rawType;
}
}
2.获取转换器
从这一步开始,我们会看到很多次的方法参数value
,这个参数就是我们最开始的JSONObject
对象
源码:
// 标准转换器
final Converter<T> converter = getConverter(type, isCustomFirst);
if (null != converter) {
return converter.convert(value, defaultValue);
}
hutool存在各种各样的转换器,用来转换许多不同的类型,比如:
转换器接口如下:
public interface Converter<T> {
/**
* 转换为指定类型<br>
* 如果类型无法确定,将读取默认值的类型做为目标类型
*
* @param value 原始值
* @param defaultValue 默认值
* @return 转换后的值
* @throws IllegalArgumentException 无法确定目标类型,且默认值为{@code null},无法确定类型
*/
T convert(Object value, T defaultValue) throws IllegalArgumentException;
/**
* 转换值为指定类型,可选是否不抛异常转换<br>
* 当转换失败时返回默认值
*
* @param value 值
* @param defaultValue 默认值
* @param quietly 是否静默转换,true不抛异常
* @return 转换后的值
* @since 5.8.0
* @see #convert(Object, Object)
*/
default T convertWithCheck(Object value, T defaultValue, boolean quietly) {
try {
return convert(value, defaultValue);
} catch (Exception e) {
if(quietly){
return defaultValue;
}
throw e;
}
}
}
如果存在转换器就把json直接转换成返回值;如果解析的过程获取不到转换器,就会继续以下流程
3.获取原生类型
源码:
// 获取原生的类型
Class<T> rowType = (Class<T>) TypeUtil.getClass(type);
if (null == rowType) {
if (null != defaultValue) {
rowType = (Class<T>) defaultValue.getClass();
} else {
// 无法识别的泛型类型,按照Object处理
return (T) value;
}
}
如果type作为ParamteredType
那么就要先获取原生类型,例如上面的Option<Value<String>>
,获取到的原生类型就是xxx.xxx.xxx.Option
,之后就是进行特殊类型转换了
4.特殊类型转换
源码:
// 特殊类型转换,包括Collection、Map、强转、Array等
final T result = convertSpecial(type, rowType, value, defaultValue);
这个方法含义也是很简单,也是相当于再次获取一下转换器
方法源码:
private <T> T convertSpecial(Type type, Class<T> rowType, Object value, T defaultValue) {
if (null == rowType) {
return null;
}
// 集合转换(不可以默认强转)
if (Collection.class.isAssignableFrom(rowType)) {
final CollectionConverter collectionConverter = new CollectionConverter(type);
return (T) collectionConverter.convert(value, (Collection<?>) defaultValue);
}
// Map类型(不可以默认强转)
if (Map.class.isAssignableFrom(rowType)) {
final MapConverter mapConverter = new MapConverter(type);
return (T) mapConverter.convert(value, (Map<?, ?>) defaultValue);
}
// Map类型(不可以默认强转)
if (Map.Entry.class.isAssignableFrom(rowType)) {
final EntryConverter mapConverter = new EntryConverter(type);
return (T) mapConverter.convert(value, (Map.Entry<?, ?>) defaultValue);
}
// 默认强转
if (rowType.isInstance(value)) {
return (T) value;
}
// 枚举转换
if (rowType.isEnum()) {
return (T) new EnumConverter(rowType).convert(value, defaultValue);
}
// 数组转换
if (rowType.isArray()) {
final ArrayConverter arrayConverter = new ArrayConverter(rowType);
return (T) arrayConverter.convert(value, defaultValue);
}
// issue#I7FQ29 Class
if("java.lang.Class".equals(rowType.getName())){
final ClassConverter converter = new ClassConverter();
return (T) converter.convert(value, (Class<?>) defaultValue);
}
// 表示非需要特殊转换的对象
return null;
}
如果不是上述的特殊类型对象,那么就会进行更加原始的转换
源码:
// 尝试转Bean
if (BeanUtil.isBean(rowType)) {
// 5直接进行转换
return new BeanConverter<T>(type).convert(value, defaultValue);
}
5.构建BeanConverter
上述new BeanConverter<T>(type)
,最终进入的构造方法是这个
源码:
/**
* 构造
*
* @param beanType 转换成的目标Bean类
* @param copyOptions Bean转换选项参数
*/
@SuppressWarnings("unchecked")
public BeanConverter(Type beanType, CopyOptions copyOptions) {
// 当前的类型,可知当前是ParameteredType
this.beanType = beanType;
// 获取当前ParameteredType的源类型,不然也无法获取class对象
this.beanClass = (Class<T>) TypeUtil.getClass(beanType);
// 掠过一些配置...
this.copyOptions = copyOptions;
}
6.使用转换器转换Json
接下来就是主要转换的代码了,源码:
@Override
@SuppressWarnings("unchecked")
public T convert(Object value, T defaultValue) {
// 获取目标类型,class对象,在这里就是Option.class了
Class<T> targetType = getTargetType();
// 这里的判断很长,其实也没啥,就是:我不知道什么类型,又没有默认返回值,那我解析个锤锤
if (null == targetType && null == defaultValue) {
throw new NullPointerException(StrUtil.format("[type] and [defaultValue] are both null for Converter [{}], we can not know what type to convert !", this.getClass().getName()));
}
// 拿默认返回值的类型作为目标类型
if (null == targetType) {
// 目标类型不确定时使用默认值的类型
targetType = (Class<T>) defaultValue.getClass();
}
// 没有value就返回默认值
if (null == value) {
return defaultValue;
}
// 判断是否已经是目标类型了
if (null == defaultValue || targetType.isInstance(defaultValue)) {
if (targetType.isInstance(value) &&
// 这个条件是排除Map及子类,因为JSONObject也是个map
!Map.class.isAssignableFrom(targetType)) {
// 除Map外,已经是目标类型,不需要转换(Map类型涉及参数类型,需要单独转换)
return targetType.cast(value);
}
// 内部业务转换*
final T result = convertInternal(value);
return ((null == result) ? defaultValue : result);
} else {
// 转换失败抛出异常
throw new IllegalArgumentException(
StrUtil.format("Default value [{}]({}) is not the instance of [{}]", defaultValue, defaultValue.getClass(), targetType));
}
}
内部解析方法:
@Override
protected T convertInternal(Object value) {
// 获取父类,判断是否是解析器,这一步掠过
final Class<?>[] interfaces = this.beanClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
if("cn.hutool.json.JSONBeanParser".equals(anInterface.getName())){
final T obj = ReflectUtil.newInstanceIfPossible(this.beanClass);
ReflectUtil.invoke(obj, "parse", value);
return obj;
}
}
// 如果是map,或者json等
if(value instanceof Map ||
value instanceof ValueProvider ||
BeanUtil.isBean(value.getClass())) {
// 如果目标类型是接口,则转换需要转为代理类,因为接口无法实例化
if(value instanceof Map && this.beanClass.isInterface()) {
// 将Map动态代理为Bean
return MapProxy.create((Map<?, ?>)value).toProxyBean(this.beanClass);
}
//转换为目标对象类型
return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();
// 以下内容也掠过
} else if(value instanceof byte[]){
return ObjectUtil.deserialize((byte[])value);
} else if(StrUtil.isEmptyIfStr(value)){
return null;
}
throw new ConvertException("Unsupported source type: {}", value.getClass());
}
7.赋值JSON内容到对象中
上述的这段代码先拆分两步,1先创建Bean赋值对象,2再进行拷贝数据
return BeanCopier.create(value, ReflectUtil.newInstanceIfPossible(this.beanClass), this.beanType, this.copyOptions).copy();
这里偷偷new出了实例对象,注意一下,在之后它会叫做target
ReflectUtil.newInstanceIfPossible(this.beanClass)
(1)创建Bean拷贝对象
BeanCopier.create
方法会调用到以下构造方法
/**
* 构造
*
* @param source 来源对象,可以是Bean或者Map
* @param target 目标Bean对象
* @param targetType 目标的泛型类型,用于标注有泛型参数的Bean对象
* @param copyOptions 拷贝属性选项
*/
public BeanCopier(Object source, T target, Type targetType, CopyOptions copyOptions) {
Assert.notNull(source, "Source bean must be not null!");
Assert.notNull(target, "Target bean must be not null!");
// 拷贝者对象
Copier<T> copier;
// 这里的来源肯定是Map,因为是JSONObject
if (source instanceof Map) {
if (target instanceof Map) {
// 目标类型如果是Map的话
copier = (Copier<T>) new MapToMapCopier((Map<?, ?>) source, (Map<?, ?>) target, targetType, copyOptions);
} else {
// json转bean的复制对象,这行情况是JSONObject会执行到的*
copier = new MapToBeanCopier<>((Map<?, ?>) source, target, targetType, copyOptions);
}
}else if(source instanceof ValueProvider){
//noinspection unchecked
copier = new ValueProviderToBeanCopier<>((ValueProvider<String>) source, target, targetType, copyOptions);
} else {
// 这个target其实是defaultValue
if (target instanceof Map) {
// 获取它的Copier
copier = (Copier<T>) new BeanToMapCopier(source, (Map<?, ?>) target, targetType, copyOptions);
} else {
// 其他情况
copier = new BeanToBeanCopier<>(source, target, targetType, copyOptions);
}
}
this.copier = copier;
}
MapToBeanCopier类的结构如下:
/**
* 构造
*
* @param source 来源Map
* @param target 目标Bean对象
* @param targetType 目标泛型类型
* @param copyOptions 拷贝选项
*/
public MapToBeanCopier(Map<?, ?> source, T target, Type targetType, CopyOptions copyOptions) {
// 设置资源(json),一个刚刚实例化的对象, 拷贝配置(掠过)
super(source, target, copyOptions);
// 针对MapWrapper特殊处理,提供的Map包装了忽略大小写的Map,则默认转Bean的时候也忽略大小写,如JSONObject(以下代码掠过)
if(source instanceof MapWrapper){
final Map<?, ?> raw = ((MapWrapper<?, ?>) source).getRaw();
if(raw instanceof CaseInsensitiveMap){
copyOptions.setIgnoreCase(true);
}
}
this.targetType = targetType;
}
(2)拷贝对象的copy
方法
以下是MapToBeanCopier
的copy
方法源码:
@Override
public T copy() {
// 获取目标类型的class
Class<?> actualEditable = target.getClass();
// 掠过该判断
if (null != copyOptions.editable) {
Assert.isTrue(copyOptions.editable.isInstance(target),
"Target class [{}] not assignable to Editable class [{}]", actualEditable.getName(), copyOptions.editable.getName());
actualEditable = copyOptions.editable;
}
// 获取成员属性的描述,就是买个json属性对应的java属性
// 例如Map.of("name", PropDesc<String>)...
// PropDesc对象结构 {
// Field f;成员属性
// Method getter; get方法
// Method setter; set方法
//}
final Map<String, PropDesc> targetPropDescMap = BeanUtil.getBeanDesc(actualEditable).getPropMap(copyOptions.ignoreCase);
// 变量JSON内容
this.source.forEach((sKey, sValue) -> {
// 获取成员属性PropDesc对象
if (null == sKey) {
return;
}
String sKeyStr = copyOptions.editFieldName(sKey.toString());
if (null == sKeyStr) {
return;
}
if (false == copyOptions.testKeyFilter(sKeyStr)) {
return;
}
// 检查目标字段可写性
final PropDesc tDesc = findPropDesc(targetPropDescMap, sKeyStr);
if (null == tDesc || !tDesc.isWritable(this.copyOptions.transientSupport)) {
// 字段不可写,跳过之
return;
}
sKeyStr = tDesc.getFieldName();
// 检查目标是否过滤属性(掠过此代码)
if (!copyOptions.testPropertyFilter(tDesc.getField(), sValue)) return;
// 获取目标字段真实类型并转换源值
final Type fieldType = TypeUtil.getActualType(this.targetType, tDesc.getFieldType());
// 转换该字段的值*
Object newValue = this.copyOptions.convertField(fieldType, sValue);
newValue = copyOptions.editFieldValue(sKeyStr, newValue);
// 目标赋值(给target的成员属性设置内容)
tDesc.setValue(this.target, newValue, copyOptions.ignoreNullValue, copyOptions.ignoreError, copyOptions.override);
});
// 返回结果,解析完毕
return this.target;
}
(3)递归转换
上述代码存在这一行:
// 转换该字段的值
Object newValue = this.copyOptions.convertField(fieldType, sValue);
进去方法体内容:
/**
* 使用自定义转换器转换字段值<br>
* 如果自定义转换器为{@code null},则返回原值。
*
* @param targetType 目标类型
* @param fieldValue 字段值
* @return 编辑后的字段值
* @since 5.8.0
*/
protected Object convertField(Type targetType, Object fieldValue) {
return (null != this.converter) ?
// 复杂的内容,如果字段是Value类型,就是本文章的例子,那么就会走这一行
this.converter.convert(targetType, fieldValue) :
// 普通值
fieldValue;
}
这里的这个转换器是 TypeConverter
:
/**
* 自定义类型转换器,默认使用全局万能转换器转换
*/
protected TypeConverter converter = (type, value) -> {
if (null == value) {
return null;
}
if (value instanceof IJSONTypeConverter) {
return ((IJSONTypeConverter) value).toBean(ObjectUtil.defaultIfNull(type, Object.class));
}
// 递归调用转换器回到第2步
return Convert.convertWithCheck(type, value, null, ignoreError);
};
四、小结
以上就是JSON转为JavaBean的过程,过程逻辑并不难。但是,一些基础的实现是比较繁琐的,如果想要更加深入的了解,可以查看hutool的源码。