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

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

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

前言

開(kāi)發(fā)業(yè)務(wù)需求時(shí),遇到了列表中包含完全不同類型的數(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,手動(dòng)解析不是不行,但肯定不推薦。如果直接使用解析工具,比如用Gson來(lái)解析的話,無(wú)論定義什么樣的數(shù)據(jù)結(jié)構(gòu)好像都不符合上面的列表元素。

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

實(shí)現(xiàn)方案

先定義好各種數(shù)據(jù)類型,與msgType字段一一對(duì)應(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)

接著自定義一個(gè)TypeAdapter。

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

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

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

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

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

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

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.交給對(duì)應(yīng)類型的TypeAdapter解析
  return adapter?.fromJsonTree(jsonObject)
 }
}

write()方法沒(méi)什么好說(shuō)的,直接交給對(duì)應(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)
 }
}

接著就是實(shí)現(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中該類型對(duì)應(yīng)的TypeAdapter
  return SubTypeAdapterWrapper(clazz, gson.getDelegateAdapter(skipPast, TypeToken.get(clazz)))
 }

邏輯也比較簡(jiǎn)單。需要注意的是,在getTypeAdapter()方法中,要將TypeAdapter<out BaseMessage>轉(zhuǎn)換成TypeAdapter<BaseMessage>。接下來(lái)我們看看SubTypeAdapterWrapper是怎樣實(shí)現(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`)
 }
}

其實(shí)就是一個(gè)包裝類。將解析R類型的TypeAdapter包裝成解析T類型的TypeAdapter。

最后就是實(shí)現(xiàn)一個(gè)TypeAdapterFactory,并將其注冊(cè)到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>)
 }
}

寫(xiě)個(gè)測(cè)試用例測(cè)試一下

抽象封裝

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

定義一個(gè)新的TypeAdapter子類。將類型和TypeAdapter的關(guān)系用Map來(lái)存儲(chǔ),并提供方法給外部調(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")
  // 注冊(cè)各種類型
  adapter.addSubTypeAdapter("text", TextMessage::class.java)
  adapter.addSubTypeAdapter("image", ImageMessage::class.java)
  adapter.addSubTypeAdapter("record", RecordMessage::class.java)
  return adapter as (TypeAdapter<T>)
 }
}

總結(jié)

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

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

相關(guān)文章

  • Mac Android Studio安裝圖文教程

    Mac Android Studio安裝圖文教程

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

    Android權(quán)限機(jī)制帶來(lái)的一些安全問(wèn)題介紹

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

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

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

    Android來(lái)電攔截的實(shí)現(xiàn)方法

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

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

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

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

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

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

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

    Android之FanLayout制作圓弧滑動(dòng)效果

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

    Android實(shí)現(xiàn)簡(jiǎn)單的popupwindow提示框

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

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

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

最新評(píng)論

会宁县| 肥西县| 韶山市| 如东县| 巴塘县| 洪湖市| 昌宁县| 南阳市| 珠海市| 聂荣县| 昌宁县| 集贤县| 河东区| 犍为县| 德清县| 涪陵区| 江阴市| 夹江县| 高清| 罗田县| 溧水县| 太谷县| 通江县| 乐业县| 体育| 麻江县| 文化| 桓仁| 南投县| 射阳县| 衡山县| 伊川县| 延边| 色达县| 顺平县| 鹤岗市| 澄江县| 太原市| 明溪县| 仙桃市| 格尔木市|