Json转为Bean的原理

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);
}
1695006980216.png

三、转换过程分析

源码:

default <T> T toBean(TypeReference<T> reference) {
    return toBean(reference.getType());
}

1.类型获取

TypeReferencegetType可以获取到一个参数化的类型实现ParamerteredTypeImpl

1695007258602.png

类的结构大致如下:

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存在各种各样的转换器,用来转换许多不同的类型,比如:

1695008141873.png

转换器接口如下:

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方法

以下是MapToBeanCopiercopy方法源码:

@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的源码。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容