gson反序列化抽象类的尝试

本文主要尝试对类似报文结构的json做反序列化,报文比如:

public class Msg {
    public static final int MSG_TYPE_SPEAK = 0;
    public static final int MSG_TYPE_COUNT = 1;
    @IntDef({MSG_TYPE_SPEAK, MSG_TYPE_COUNT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MsgType {}

    private int type;

    private AbsMsgBody body;

    public Msg(int type, AbsMsgBody body) {
        this.type = type;
        this.body = body;
    }

    public @MsgType int getType(){
        return type;
    }

    public AbsMsgBody getBody() {
        return body;
    }
}

其中msgType决定了后面body这个抽象类字段具体是那种实例。下面是body:

public abstract class AbsMsgBody {
}

public class MsgBodySpeak extends AbsMsgBody{
    private String words;

    public MsgBodySpeak(String words) {
        this.words = words;
    }

    public String getWords() {
        return words;
    }
}

public class MsgBodyCount extends AbsMsgBody {
    private long count;

    public MsgBodyCount(int count) {
        this.count = count;
    }

    public long getCount() {
        return count;
    }
}

 

之前有做过使gson解析更健壮的尝试,解决服务端下发数据与实际类型冲突导致解析失败的问题:

https://blog.csdn.net/starry_eve/article/details/100546877

今天直接尝试对抽象类进行解析,有点野。

从上一篇文章可以知道,gson反序列化过程中,解析对象中的对象字段,会递归调用TypeAdapter,每个字段的解析实际就是对字段类型所对应的TypeAdapter的read方法的调用。

所以我们的思路就是,拦截TypeAdapter的方法,如果能提前获取到msgType,稍后则可以知道要把AbsMsgBody这个TypeAdapter替换成哪个派生类的TypeAdapter。

今天使用的核心方法Gson#getDelegateAdapter:

public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
  ...
}

这个方法允许使用者根据type来创建默认的TypeAdapter。

第二个方法GsonBuilder#registerTypeAdapterFactory

  public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
    ...
  }

这个方法允许使用者自定义TypeAdapter的创建。

 

下面配合使用两者,先创建一个Factory实例:

public class AbsTypeAdapterFactory implements TypeAdapterFactory {

    private int msgType;

    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
        return new TypeAdapter<T>() {
            public void write(JsonWriter out, T value) throws IOException {
                getDelegate(gson, type).write(out, value);
            }

            public T read(JsonReader in) throws IOException {
                TypeToken<T> typeToken = type;

                String typeName = type.getRawType().getName();
                if (typeName.equals(AbsMsgBody.class.getName())){
                    typeToken = getTypeToken(msgType);
                }

                T result = getDelegate(gson, typeToken).read(in);

                // 如果是消息类型尝试保存
                if (typeName.equals(int.class.getName())){
                    msgType = (Integer) result;
                }

                return result;
            }
        };
    }

    private <T> TypeToken<T> getTypeToken(int msgType){
        switch (msgType){
            case Msg.MSG_TYPE_SPEAK:
                return (TypeToken<T>) new TypeToken<MsgBodySpeak>(){};
            case Msg.MSG_TYPE_COUNT:
                return (TypeToken<T>) new TypeToken<MsgBodyCount>(){};
        }
        throw new IllegalArgumentException();
    }

    private <T> TypeAdapter<T> getDelegate(Gson gson, TypeToken<T> type){
        return gson.getDelegateAdapter(this, type);
    }
}

首先如果解析int类型,我们知道它是msgType,把它保存起来(这里偷个懒,因为只有一个int,实际使用需要特殊处理)。

如果解析AbsMsgBody类型,则使用之前保存起来的msgType来决定到底实际是解析成哪种实现类。

然后在使用时注册到gson:

        /*
         * 反序列化
         */
        Msg miscFromJson = null;
        try{
            miscFromJson = new GsonBuilder()
                    .registerTypeAdapterFactory(new AbsTypeAdapterFactory())
                    .create()
                    .fromJson(json, Msg.class);
        }catch (Exception e){
            Log.e(TAG, Log.getStackTraceString(e));
        }
        Log.i(TAG, "decode => " + new Gson().toJson(miscFromJson));

我们传入的json值为:

{
	"type": 1,
	"body": {
		"count": 12
	}
}

执行:

decode => {"body":{"count":12},"type":1}

结果是gson成功解析了抽象类对象。

但不要高兴得太早,其实这种方法不太“靠谱”,为什么呢?

1.我们知道gson通过JsonReader对象进行顺序解析,所以,一旦type在body的后面,我们就无法知道type,也无法正确地解析body

2.即使这样,使用这种奇技淫术,我们还要求:type和body中的字段不能在一个类中,比如type和count在一个类,我们不可能解析一个类两次,因为在解析count之前就要知道type

 

所以结论就是,gson是可以反序列化抽象类的,但是限制太多,真正在项目中基本无法实施,如果有大佬有啥建议可以指点下小老弟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值