最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android如何解析異構(gòu)列表

 更新時間:2021年03月19日 09:12:55   作者:benio  
這篇文章主要介紹了Android如何解析異構(gòu)列表,幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下

前言

開發(fā)業(yè)務(wù)需求時,遇到了列表中包含完全不同類型的數(shù)據(jù)結(jié)構(gòu)。這種列表我們稱為異構(gòu)列表。以聊天記錄列表為例

[
	{
		"msgType" : "text",
		"id" : "1",
		"content" : "Hello world"
	},
	{
		"msgType" : "record",
		"id" : "2",
		"url" : "https://xxxx.mp4",
		"length" : 123450
	},
	{
		"msgType" : "image",
		"id" : "3",
		"url" : "https://xxxx.jpg",
		"size" : "300x300"
	}
]

要想解析上面的JSON,手動解析不是不行,但肯定不推薦。如果直接使用解析工具,比如用Gson來解析的話,無論定義什么樣的數(shù)據(jù)結(jié)構(gòu)好像都不符合上面的列表元素。

那可不可以做到,我們告訴Gson列表中各個元素分別是什么樣的數(shù)據(jù)類型,這樣它不就知道該怎么解析了嗎?接下來我們通過Gson的TypeAdapter來實現(xiàn)自定義解析。

實現(xiàn)方案

先定義好各種數(shù)據(jù)類型,與msgType字段一一對應(yīng)

abstract class BaseMessage(val id: String?, val msgType: String?)

class TextMessage(id: String?, msgType: String?, val content: String?
) : BaseMessage(id, msgType)

class ImageMessage(id: String?, msgType: String?, val url: String?, val size: String?
) : BaseMessage(id, msgType)

class RecordMessage(id: String?, msgType: String?, val url: String?, val length: Long
) : BaseMessage(id, msgType)

接著自定義一個TypeAdapter。

class BaseMessageTypeAdapter : TypeAdapter<BaseMessage>() {
 override fun write(out: JsonWriter, value: BaseMessage?) {
 }

 override fun read(`in`: JsonReader): BaseMessage? {
 }
}

可以看到里面有兩個方法:write()負(fù)責(zé)序列化,read()負(fù)責(zé)反序列化。我們先重點關(guān)注read()的實現(xiàn)

實現(xiàn)read()的基本思路如下

  1. 讀取msgType字段
  2. 根據(jù)msgType判斷對應(yīng)的數(shù)據(jù)類型
  3. 根據(jù)該數(shù)據(jù)類型獲取解析該類型的TypeAdapter
  4. 交給對應(yīng)類型的TypeAdapter解析

依照上述思路,可以寫出read()的基本實現(xiàn)代碼。當(dāng)然這是比較粗糙的實現(xiàn),實際上還有其他情況要考慮

class BaseMessageTypeAdapter(private val gson: Gson, 
        private val skipPast: TypeAdapterFactory
) : TypeAdapter<BaseMessage>() {
 override fun read(`in`: JsonReader): BaseMessage? {
  // 1.讀取msgType字段
  val jsonObject = Streams.parse(`in`).asJsonObject
  val msgType = jsonObject.get("msgType")?.asString
  // 2.根據(jù)msgType獲取解析該類型的TypeAdapter
  val adapter = getTypeAdapterByType(msgType)
  // 3.交給對應(yīng)類型的TypeAdapter解析
  return adapter?.fromJsonTree(jsonObject)
 }
}

write()方法沒什么好說的,直接交給對應(yīng)類型的TypeAdapter序列化

class BaseMessageTypeAdapter(private val gson: Gson, 
        private val skipPast: TypeAdapterFactory
) : TypeAdapter<BaseMessage>() {
 override fun write(out: JsonWriter, value: BaseMessage?) {
  if (value == null) {
   out.nullValue()
   return
  }
  getTypeAdapterByType(value.msgType)?.write(out, value)
 }
}

接著就是實現(xiàn)getTypeAdapterByType()方法。

 private fun getTypeAdapterByType(type: String?): TypeAdapter<BaseMessage>? {
  return when (type) {
   "text" -> getTypeAdapter(TextMessage::class.java)
   "image" -> getTypeAdapter(ImageMessage::class.java)
   "record" -> getTypeAdapter(RecordMessage::class.java)
   else -> null
  }
 }

 private fun <R : BaseMessage> getTypeAdapter(clazz: Class<R>): TypeAdapter<BaseMessage> {
  // 獲取Gson中該類型對應(yīng)的TypeAdapter
  return SubTypeAdapterWrapper(clazz, gson.getDelegateAdapter(skipPast, TypeToken.get(clazz)))
 }

邏輯也比較簡單。需要注意的是,在getTypeAdapter()方法中,要將TypeAdapter<out BaseMessage>轉(zhuǎn)換成TypeAdapter<BaseMessage>。接下來我們看看SubTypeAdapterWrapper是怎樣實現(xiàn)的

class SubTypeAdapterWrapper<T, R : T>(private val clazz: Class<R>,
          private val adapter: TypeAdapter<R>
) : TypeAdapter<T>() {
 override fun write(out: JsonWriter, value: T) {
  if (!clazz.isInstance(value)) {
   throw JsonSyntaxException("Expected a " + clazz.name + " but was " + value)
  }
  adapter.write(out, value as R)
 }

 override fun read(`in`: JsonReader): T {
  return adapter.read(`in`)
 }
}

其實就是一個包裝類。將解析R類型的TypeAdapter包裝成解析T類型的TypeAdapter。

最后就是實現(xiàn)一個TypeAdapterFactory,并將其注冊到Gson

class BaseMessageTypeAdapterFactory : TypeAdapterFactory {
 override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
  if (!BaseMessage::class.java.isAssignableFrom(type.rawType)) {
   return null
  }
  return BaseMessageTypeAdapter(gson, this) as (TypeAdapter<T>)
 }
}

寫個測試用例測試一下

抽象封裝

為了更好的復(fù)用,接下來我們要對BaseMessageTypeAdapter改造一下。

定義一個新的TypeAdapter子類。將類型和TypeAdapter的關(guān)系用Map來存儲,并提供方法給外部調(diào)用。

public class HeterogeneousTypeAdapter<T> extends TypeAdapter<T> {
 private final Gson mGson;
 private final TypeAdapterFactory mSkipPast;
 private final String mFieldName;
 private final Map<String, TypeAdapter<T>> mClassToAdapterMap = new HashMap<>();
 private final Map<String, TypeAdapter<T>> mFieldToAdapterMap = new HashMap<>();

 public HeterogeneousTypeAdapter(Gson gson, TypeAdapterFactory skipPast, String fieldName) {
  mGson = gson;
  mSkipPast = skipPast;
  mFieldName = fieldName;
 }

 public <R extends T> void addSubTypeAdapter(final String fieldValue,
            final Class<R> cls) {
  final TypeAdapter<R> typeAdapter = mGson.getDelegateAdapter(mSkipPast, TypeToken.get(cls));
  addSubTypeAdapter(fieldValue, cls, typeAdapter);
 }

 public <R extends T> void addSubTypeAdapter(final String fieldValue,
            final Class<R> cls,
            final TypeAdapter<R> typeAdapter) {
  final TypeAdapter<T> adapter = new SubTypeAdapterWrapper<>(cls, typeAdapter);
  mClassToAdapterMap.put(cls.getName(), adapter);
  mFieldToAdapterMap.put(fieldValue, adapter);
 }

 @Override
 public void write(JsonWriter out, T value) throws IOException {
  if (value == null) {
   out.nullValue();
   return;
  }
  getTypeAdapterByClass(value.getClass()).write(out, value);
 }

 @Override
 public T read(JsonReader in) throws IOException {
  if (in.peek() == JsonToken.NULL) {
   in.nextNull();
   return null;
  }

  final JsonObject jsonObject = Streams.parse(in).getAsJsonObject();
  final JsonElement fieldElement = jsonObject.get(mFieldName);
  if (fieldElement == null || fieldElement.isJsonNull()) {
   throw new JsonSyntaxException("Field " + mFieldName + " is null or not found");
  }

  final String field = fieldElement.getAsJsonPrimitive().getAsString();
  final TypeAdapter<T> adapter = getTypeAdapterByField(field);
  if (adapter == null) {
   // Unknown field, just skip
   return null;
  }
  return adapter.fromJsonTree(jsonObject);
 }

 private TypeAdapter<T> getTypeAdapterByClass(Class<?> cls) {
  TypeAdapter<T> adapter = mClassToAdapterMap.get(cls.getName());
  if (adapter == null) {
   throw new JsonParseException("Unknown class : " + cls);
  }
  return adapter;
 }

 private TypeAdapter<T> getTypeAdapterByField(String field) {
  return mFieldToAdapterMap.get(field);
 }
}

使用方式

class BaseMessageTypeAdapterFactory : TypeAdapterFactory {
 override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
  if (!BaseMessage::class.java.isAssignableFrom(type.rawType)) {
   return null
  }
  val adapter = HeterogeneousTypeAdapter<BaseMessage>(gson, this, "msgType")
  // 注冊各種類型
  adapter.addSubTypeAdapter("text", TextMessage::class.java)
  adapter.addSubTypeAdapter("image", ImageMessage::class.java)
  adapter.addSubTypeAdapter("record", RecordMessage::class.java)
  return adapter as (TypeAdapter<T>)
 }
}

總結(jié)

通過自定義TypeAdapter,我們實現(xiàn)了解析異構(gòu)列表的功能。免除手動解析的繁瑣工作,避免出現(xiàn)不必要的錯誤。

以上就是Android如何解析異構(gòu)列表的詳細(xì)內(nèi)容,更多關(guān)于Android 解析異構(gòu)列表的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Mac Android Studio安裝圖文教程

    Mac Android Studio安裝圖文教程

    本文主要介紹了Mac Android Studio安裝教程,文中通過圖文代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Android權(quán)限機制帶來的一些安全問題介紹

    Android權(quán)限機制帶來的一些安全問題介紹

    這篇文章主要介紹了Android權(quán)限機制帶來的一些安全問題介紹,本文講解了權(quán)限機制的缺陷和不足、樹立權(quán)限意識、越過權(quán)限機制等內(nèi)容,需要的朋友可以參考下
    2015-04-04
  • 關(guān)于Android 6.0權(quán)限的動態(tài)適配詳解

    關(guān)于Android 6.0權(quán)限的動態(tài)適配詳解

    Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用戶體驗, 同時也為程序員帶來新的負(fù)擔(dān). 動態(tài)權(quán)限管理就是這樣, 一方面讓用戶更加容易的控制自己的隱私, 一方面需要重新適配應(yīng)用權(quán)限,本文介紹了關(guān)于Android 6.0權(quán)限動態(tài)適配的相關(guān)資料,需要的朋友可以參考下。
    2017-11-11
  • Android來電攔截的實現(xiàn)方法

    Android來電攔截的實現(xiàn)方法

    這篇文章主要為大家詳細(xì)介紹了Android來電攔截的實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • android圖像繪制(一)多種方法做圖像鏡像

    android圖像繪制(一)多種方法做圖像鏡像

    在android中做圖像鏡像有很多方法,今天算是學(xué)習(xí)了,感興趣的朋友可以參考下
    2013-01-01
  • android實現(xiàn)圖片裁剪的兩種方法

    android實現(xiàn)圖片裁剪的兩種方法

    這篇文章主要為大家詳細(xì)介紹了android實現(xiàn)圖片裁剪的兩種方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Android的權(quán)限設(shè)置及自啟動設(shè)置方法

    Android的權(quán)限設(shè)置及自啟動設(shè)置方法

    今天小編就為大家分享一篇Android的權(quán)限設(shè)置及自啟動設(shè)置方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Android之FanLayout制作圓弧滑動效果

    Android之FanLayout制作圓弧滑動效果

    這篇文章主要介紹了Android之FanLayout制作圓弧滑動效果,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-08-08
  • Android實現(xiàn)簡單的popupwindow提示框

    Android實現(xiàn)簡單的popupwindow提示框

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)簡單的popupwindow提示框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Android自定義View實現(xiàn)簡單文字描邊功能

    Android自定義View實現(xiàn)簡單文字描邊功能

    這篇文章主要為大家詳細(xì)介紹了Android自定義View實現(xiàn)簡單文字描邊功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12

最新評論

微山县| 遂宁市| 马鞍山市| 惠东县| 喀喇沁旗| 本溪| 阳原县| 邵武市| 阜南县| 宜兴市| 额济纳旗| 绍兴县| 黄冈市| 沅江市| 朝阳县| 西峡县| 喀喇| 临江市| 临猗县| 策勒县| 乐东| 崇左市| 沁水县| 涞水县| 舒城县| 慈溪市| 新余市| 曲水县| 九台市| 密云县| 北京市| 山西省| 仙游县| 庄河市| 白河县| 新河县| 榆树市| 湘潭市| 宜黄县| 左权县| 普兰县|