Android下谈谈Gson常见优化

2020-06-08  本文已影响0人  落后程序员

在 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

上一篇下一篇

猜你喜欢

热点阅读