技术标签: android
本文主要尝试对类似报文结构的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是可以反序列化抽象类的,但是限制太多,真正在项目中基本无法实施,如果有大佬有啥建议可以指点下小老弟。
文章浏览阅读857次。通过这篇文章可以让大家更好的理解sizeof而不至于闹出sizeof是函数的笑话。
文章浏览阅读2.9k次,点赞4次,收藏37次。@ 图像阈值分割(最大熵方法)老规矩,看相关函数(哈哈,没有啥函数)步骤1.进行归一化直方图2.累加概率直方图3.求出各个灰度级的熵4.计算最大熵时的阈值计算公式1.normHist为归一化的直方图,这里不做介绍2.累加概率直方图3.求出各个灰度级的熵4.计算最大熵时的阈值计算:f(t)=f1(t)+f2(t)最大化的t值,该值即为得到的阈值,即thresh=argmax(f(t))上代码#相关包import numpy as npimport cv2import im_python 最大熵阈值法
文章浏览阅读2.2w次,点赞3次,收藏16次。使用electron开发时,实现记住密码功能。这个功能使用比较常见,electron也提供了session的模块来支持。当然session模块还有更多的用处。关于更多的session模块的使用,请查看Electron文档。还有很多其他的实现方式,比如使用一个json文件来存放用户名密码等。_electron 记住密码
文章浏览阅读2.1k次。多数据源多租户-动态切换数据源
文章浏览阅读809次。在网页制作中增加微信QQ微博等ICO-font图标字体符号window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)..._weibo wechat font
文章浏览阅读1.2k次,点赞2次,收藏3次。解析全局变量,函数,局部变量并按一定格式保存 点击(此处)折叠或打开 #include sys/types.h> #include sys/stat.h> #include fcntl.h> #include stdlib.h>_dwarf.h
文章浏览阅读114次。ssh web 应用使用c3p0数据库连接池ibm 给出的实例说明 将ssh web应用程序从tomcat迁移到websphere(was)WAS 中数据源的配置使用及其常见问题开源DBCP、C3P0、Proxool 、 BoneCP连接池的比较 ..._was数据源 ssh
文章浏览阅读3.4k次。Intent intent = new Intent(this,Main2Activity.class); PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0); builder.setContentIntent(pending); builder.setOngoi_adb 发送上下一曲按键
文章浏览阅读6.4k次,点赞11次,收藏78次。1、.netCore的执行过程2、如何在controller中注入service?在Config Services方法中配置这个service在Controller的构造函数中 添加这个依赖注入3、.netCore比.net更具优势的地方是什么?跨平台,可以运行在 Windows 、Linux 和 MAC 系统上对框架本身安装没有依赖,所有依赖都和程序本身在一起.netCore处理请求的效率更高,进而可以处理更多的请求具有更多的安装配置方法4、.netCore主要的特性有哪些?依赖注入_.netcore
文章浏览阅读2.5k次。题意程序设计思维作业和实验使用的实时评测系统,具有及时获得成绩排名的特点,那它的功能是怎么实现的呢?我们千辛万苦怼完了不忍直视的程序并提交以后,评测系统要么返回AC,要么是返回各种其他的错误,不论是怎样的错法,它总会给你记上一笔,表明你曾经在这儿被坑过,而当你历经千辛终将它AC之后,它便会和你算笔总账,表明这题共错误提交了几次。在岁月的长河中,你通过的题数虽然越来越多,但通过每题时你所共花去..._++编程考试使用的实时提交系统,具有即时获得成绩排名的特点。它的功能是怎么实现
文章浏览阅读1.2k次。头部文件调用<?php require_once('header.php'); ?> 底部文件调用<?php require_once('footer.php'); ?>关于我们图片调用<?php $row = $dosql->GetOne("SELECT * FROM `#@__info` W..._phpmywind首页关于我们怎么调用
文章浏览阅读2.2k次。[巨坑]读取不到Nacos配置中心的内容!这真的太逆天了,我服了这是出错的配置:报错内容:确定其他都没有问题,但就是读不到配置。这是正确的配置:excuse me ??? 换了无数个SpringCloud SpringBoot 和 Nacos的版本,windows平台linux平台全部试过去,结果就是因为这个写错了(而且我还不知道这到底是错哪了,有什么区别…)浪费了我三个小时时间,大家有则改之,无则加勉。..._linux中部署jar包读取不到nacos配置文件的内容