Android下谈谈Gson常见优化

在 JSON 中,值必须是以下数据类型之一:
字符串
数字
对象(JSON 对象)
数组
布尔
Null
JSON 的值不可以是以下数据类型之一:
函数
日期
undefined

参考链接:JSON 数据类型

后台返回数据,有的时候受语言限制或者代码没写好的原因,该返回字符串的但是返回null。返回number的但返回字符串,导致客户端序列化对象的时候出现异常

举个栗子,json数据如下:

{
  "tempInt":"",
  "tempString":1111,
  "tempAs":[
    {
      "tempInt":"",
      "tempString":1111
    }
  ],
  "tempA":{
    "tempInt":"",
    "tempString":1111
  }
}

对应的kotlin对象如下

class TestData {
    var tempInt: Int? = 0
    var tempString: String? = ""
    var tempA: A? = null
    var tempAs: List<A>? = null

    class A {
        var tempInt: Int? = 0
        var tempString: String? = ""
        override fun toString(): String {
            return "A(tempInt=$tempInt, tempString=$tempString)"
        }
    }
}

测试代码如下:

    @JvmStatic
    fun main(args: Array<String>) {
        val testData = gson.fromJson(
            "{\n" +
                    "  \"tempInt\":\"\",\n" +
                    "  \"tempString\":1111,\n" +
                    "  \"tempAs\":[\n" +
                    "    {\n" +
                    "      \"tempInt\":\"\",\n" +
                    "      \"tempString\":1111\n" +
                    "    }\n" +
                    "  ],\n" +
                    "  \"tempA\":{\n" +
                    "    \"tempInt\":\"\",\n" +
                    "    \"tempString\":1111\n" +
                    "  }\n" +
                    "}", TestData::class.java
        )
        System.out.println(testData.tempString + "=======" + testData.tempInt)
        System.out.println(testData.tempA?.toString() + "=======" + testData.tempA?.tempInt)
        System.out.println(testData.tempAs.toString())

    }

不处理,就会提示如下报错

Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: empty String
    at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:228)
    at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:218)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
    at com.google.gson.Gson.fromJson(Gson.java:927)
    at com.google.gson.Gson.fromJson(Gson.java:892)
    at com.google.gson.Gson.fromJson(Gson.java:841)
    at com.google.gson.Gson.fromJson(Gson.java:813)
    at com.ziqi.baselibrary.util.gson.GsonUtil.main(GsonUtil.kt:42)
Caused by: java.lang.NumberFormatException: empty String
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at com.google.gson.stream.JsonReader.nextInt(JsonReader.java:1201)
    at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:226)
    ... 8 more

但是通过GsonBuilder实现创建gson对象,并使用TypeAdapter来处理每个基本类型,就不会有上面的异常

    val gson: Gson = GsonBuilder() //配置你的Gson
        .registerTypeAdapter(Int::class.java, IntAdapter())
        .registerTypeAdapter(Integer::class.java, IntAdapter())
        .registerTypeAdapter(String::class.java, StringAdapter())
        .registerTypeAdapter(Double::class.java, DoubleAdapter())
        .create()

没有异常并输出内容

1111=======-1
A(tempInt=-1, tempString=1111)=======-1
[A(tempInt=-1, tempString=1111)]

因为一般只会用double,long,int,string这几个基本类型,我就整理封装了一下

DoubleAdapter.kt

package com.ziqi.baselibrary.util.gson

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.io.IOException

class DoubleAdapter : TypeAdapter<Double?>() {
    @Throws(IOException::class)
    override fun write(out: JsonWriter?, value: Double?) {
        try {
            var tempValue = value
            if (tempValue == null) {
                tempValue = -1.0
            }
            out?.value(tempValue)
        } catch (e: Exception) {

        }
    }

    @Throws(IOException::class)
    override fun read(`in`: JsonReader): Double? {
        var value: Double? = -1.0
        try {
            if (`in`.peek() == JsonToken.NULL) {
                `in`.nextNull()
                value = -1.0
            } else if (`in`.peek() == JsonToken.BOOLEAN) {
                val b = `in`.nextBoolean()
                value = if (b) 1.0 else 0.0
            } else if (`in`.peek() == JsonToken.STRING) {
                try {
                    value = `in`.nextString().toDouble()
                } catch (e: Exception) {
                    value = -1.0
                }
            } else {
                value = `in`.nextDouble()
            }
        } catch (e: Exception) {
            value = -1.0
        }
        return value
    }
}

IntAdapter.kt

package com.ziqi.baselibrary.util.gson

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.io.IOException

class IntAdapter : TypeAdapter<Int?>() {
    @Throws(IOException::class)
    override fun write(out: JsonWriter?, value: Int?) {
        try {
            var tempValue = value
            if (tempValue == null) {
                tempValue = -1
            }
            out?.value(tempValue)
        } catch (e: Exception) {

        }
    }

    @Throws(IOException::class)
    override fun read(`in`: JsonReader): Int? {
        var value: Int? = -1
        try {
            if (`in`.peek() == JsonToken.NULL) {
                `in`.nextNull()
                value = -1
            } else if (`in`.peek() == JsonToken.BOOLEAN) {
                val b = `in`.nextBoolean()
                value = if (b) 1 else 0
            } else if (`in`.peek() == JsonToken.STRING) {
                try {
                    value = `in`.nextString().toInt()
                } catch (e: Exception) {
                    value = -1
                }
            } else {
                value = `in`.nextInt()
            }
        } catch (e: Exception) {
            value = -1
        }
        return value
    }
}

LongAdapter.kt

package com.ziqi.baselibrary.util.gson

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.io.IOException

class LongAdapter : TypeAdapter<Long?>() {
    @Throws(IOException::class)
    override fun write(out: JsonWriter?, value: Long?) {
        try {
            var tempValue = value
            if (tempValue == null) {
                tempValue = -1
            }
            out?.value(tempValue)
        } catch (e: Exception) {

        }
    }

    @Throws(IOException::class)
    override fun read(`in`: JsonReader): Long? {
        var value: Long? = -1
        try {
            if (`in`.peek() == JsonToken.NULL) {
                `in`.nextNull()
                value = -1
            } else if (`in`.peek() == JsonToken.BOOLEAN) {
                val b = `in`.nextBoolean()
                value = if (b) 1 else 0
            } else if (`in`.peek() == JsonToken.STRING) {
                try {
                    value = `in`.nextString().toLong()
                } catch (e: Exception) {
                    value = -1
                }
            } else {
                value = `in`.nextLong()
            }
        } catch (e: Exception) {
            value = -1
        }
        return value
    }
}

StringAdapter.kt

package com.ziqi.baselibrary.util.gson

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter

class StringAdapter : TypeAdapter<String?>() {
    override fun write(out: JsonWriter?, value: String?) {
        try {
            var tempValue: String? = value
            if (tempValue == null) {
                tempValue = ""
            }
            out?.value(tempValue)
        } catch (e: Exception) {

        }
    }

    override fun read(`in`: JsonReader?): String? {
        var value: String? = ""
        try {
            if (`in`?.peek() == JsonToken.NULL) {
                `in`.nextNull()
                value = ""
            } else if (`in`?.peek() == JsonToken.BOOLEAN) {
                val b = `in`.nextBoolean()
                value = b.toString()
            } else if (`in`?.peek() == JsonToken.NUMBER) {
                try {
                    value = `in`.nextLong().toString()
                } catch (e: Exception) {
                    value = ""
                }
            } else {
                value = `in`?.nextString()
            }
        } catch (e: Exception) {
            value = ""
        }
        return value
    }
}

解决了基本的类型,还有2个对象没处理

对象(JSON 对象)
数组

使用JsonDeserializer接口类,实现判断isJsonArrayisJsonObject
修改后的Gson对象代码

val gson: Gson = GsonBuilder() //配置你的Gson
        .setDateFormat("yyyy-MM-dd hh:mm:ss")
        //https://blog.csdn.net/axxbc123/article/details/84625539
        .enableComplexMapKeySerialization()
        //https://blog.csdn.net/u010502101/article/details/80555558
        .serializeNulls()
        .registerTypeAdapter(Int::class.java, IntAdapter())
        .registerTypeAdapter(Integer::class.java, IntAdapter())
        .registerTypeAdapter(String::class.java, StringAdapter())
        .registerTypeAdapter(Double::class.java, DoubleAdapter())
        .registerTypeAdapter(Long::class.java, LongAdapter())
        .registerTypeAdapter(List::class.java, ListJsonDeserializer())
        .registerTypeAdapter(TestData.A::class.java, ObjectJsonDeserializer<TestData.A>())
        .create()

ListJsonDeserializer.java

package com.ziqi.baselibrary.util.gson;

import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * https://www.jianshu.com/p/3108f1e44155
 */
public class ListJsonDeserializer implements JsonDeserializer<List<?>> {
    @Override
    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json.isJsonArray()) {
            JsonArray array = json.getAsJsonArray();
            Type itemType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
            List list = new ArrayList<>();
            for (int i = 0; i < array.size(); i++) {
                JsonElement element = array.get(i);
                Object item = context.deserialize(element, itemType);
                list.add(item);
            }
            return list;
        }
        return Collections.EMPTY_LIST;
    }
}

ObjectJsonDeserializer.java

package com.ziqi.baselibrary.util.gson;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ObjectJsonDeserializer<T> implements JsonDeserializer<T> {
    @Override
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        T temp = null;
        if (json.isJsonObject()) {
            temp = GsonUtil.INSTANCE.getInnerGson().fromJson(json, typeOfT);
            return temp;
        }
        return temp;
    }
}

核心技术点是使用TypeAdapter和JsonDeserializer两个类处理,gson还有很多api可以让我学习的,若有更好的封装代码,希望大佬分享出来

经过以上处理gson后,不管后台返回对象是否正常,我们代码就会变得更健壮起来,最近我在写一个玩Android 这完整代码会在我GitHub上的工程上
WanAndroidKotlin
我是分了模块开发的,在baselibrary模块里面

转载请标明原处:https://www.jianshu.com/p/4a70eebedf68

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