JSON数据解析一键适配PHP

和服务端小伙伴联调接口时总会遇到json格式的问题,导致客户端无法正常解析数据,出现数据解析异常。本文从客户端角度出发规避解析数据异常,避免重复字段和空指针异常,减少服务端小伙伴的工作量,增加客户端的稳定性

一,有数据时和无数据时返回数据类型不一致,一般发生在json对象和json数组
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]          有数据时
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": false,
        "strings": {}          无数据时
    }

此时客户端解析json时strings字段就会数据类型异常,有数据时是json数组,无数据时是json对象。还有一种类似情况如下

    {
        "name": "张三",
        "age": 22,
        "avatar": {           有数据时
               "large": "https://pic4.zhimg.com/v2-e552164d8e4ad9beca6eb50140df75b5_b.jpg",
               "small": "https://pic4.zhimg.com/v2-e552164d8e4ad9beca6eb50140df75b5_b.jpg"
        },
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": [],          无数据时
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }
二,返回非数值类型时解析数据异常。
    {
        "name": "张三",
        "age": "22",        字符串类型
        "avatar": null,
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "name": "张三",
        "age": 22,        整型类型
        "avatar": null,
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "name": "张三",
        "age": 22.5,         浮点类型
        "avatar": null,
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }

对于age字段就应该是一个整数类型,第一种返回一个字符串“22”,第三种返回浮点数22.5,客户端都会类型强转异常。

三,布尔类型数据解析异常
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": false,         布尔类型
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": "false",         字符串类型
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": 0,         数值类型
        "strings": ["12312", "哈哈", "3213"]
    }

对于isRegister字段就应该是一个布尔类型,第二种返回一个字符串“false”,第三种返回整数类型0,客户端都会类型强转异常。

四,同一字段在不同接口返回的key不同
    {
        "id": 212,         用户ID
        "age": 22,
        "avatar": null,
        "isRegister": false,
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "uid": 212,         这也是用户ID
        "age": 22,
        "avatar": null,
        "isRegister": "false",
        "strings": ["12312", "哈哈", "3213"]
    }
    {
        "user_id": 212,        这还是用户ID
        "age": 22,
        "avatar": null,
        "isRegister": 0,
        "strings": ["12312", "哈哈", "3213"]
    }

对于用户ID这个字段时而返回id、时而uid、时而user_id,增加客户端解析难度和误解,如果不慎取错就只能是默认值0或者“”或者null,业务执行逻辑就会错误或者崩溃

五,json数组中返回null的对象,这是最可怕的一种,线上已经验证过。
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": false,
        "strings": null         会验证空指针,客户端不会崩溃
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": "false",
        "strings": []         会验证数组长度,客户端不会崩溃
    }
    {
        "name": "张三",
        "age": 22,
        "avatar": null,
        "isRegister": 0,
        "strings": ["哈哈", null, "3213"]         不会验证数组中的顶级对象是否为null,客户端会崩溃
    }

对于json数组返回null或者[],客户端都会验证不会发生崩溃,如果返回["哈哈", null, "3213"],因为客户端不会验证数组中的顶级对象是否为null,所以会崩溃

针对上述五种异常,客户端分别对应五种解决方式。服务端小伙伴再也不用不厌其烦的改动,客户端一处配置处处生效,无需重复代码,避免解析异常,减少崩溃。

一,有数据时和无数据时返回数据类型不一致 and json数组中返回null的对象

(1)针对json对象 registerTypeAdapterFactory(new ObjectTypeAdapterFactory())GsonBuilder,注册ObjectTypeAdapterFactory对象,代理解析json对象

    try {
        JsonElement tree = elementAdapter.read(in);
        if (delegate instanceof Adapter) {
            if (!tree.isJsonObject()) {        判断是否是json对象
                return null;         否?返回默认null,执行下一字段解析
            } else {
                return delegate.fromJsonTree(tree);        是?正常解析数据
            }
        } else {
            return delegate.fromJsonTree(tree);
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;         抓住发生的异常,返回默认值null,执行下一字段解析
    }

(2)针对json数组registerTypeAdapter(List.class, new ListDeserializer())GsonBuilder,注册ListDeserializer对象,代理解析json数组

        if (json.isJsonArray()) {         判断是否是json数组
            Gson newGson = new Gson();
            List<?> list = newGson.fromJson(json, typeOfT);         是?正常解析数据
            if (list == null) return Collections.EMPTY_LIST;         解析数组为null ? 返回默认空数组,执行下一字段解析
            list.removeAll(Collections.singleton(null));         数组为其中顶级元素为null ? 移除null元素,改变数组长度
            return list;
        } else {
            return Collections.EMPTY_LIST;        否?返回默认空数组,执行下一字段解析
        }
二,返回非数值类型时解析数据异常

registerTypeAdapter(Integer.class, new IntegerDeserializer())
registerTypeAdapter(Long.class, new LongDeserializer())
registerTypeAdapter(Float.class, new FloatDeserializer())
registerTypeAdapter(Double.class, new DoubleDeserializer())GsonBuilder,注册对应的数值类型Deserializer对象,代理解析数值类,分别解析整型,长整型,单精度浮点型,双精度浮点型

        try {
            return (int) json.getAsJsonPrimitive().getAsDouble();        默认用双精度浮点型解析在转换为想要的类型
        } catch (Exception e) {
            e.printStackTrace();
            return 0;    出现异常返回默认值0,执行下一字段解析
        }
三,布尔类型数据解析异常

registerTypeAdapter(Boolean.class, new BooleanSerializer())GsonBuilder,注册BooleanSerializer对象,代理解析布尔类型

        try {
            if (json.getAsJsonPrimitive().isNumber()) {        是否返回的是数值类型
                return json.getAsJsonPrimitive().getAsDouble() != 0F;         是 ? 数值 == 0 时 为 false  非 0 为 true
            } else {
                return json.getAsJsonPrimitive().getAsBoolean();          否直接解析布尔值
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;    出现异常返回默认值false,执行下一字段解析
        }
四,同一字段在不同接口返回的key不同

此种情况需要为单独的解析实体类配置。例如搜索接口返回的结果,@SerializedName可配置多种字段,客户端只用一个字段接收,任你服务端千变万化,我自客户端岿然不动。

    @SerializedName(value = "title", alternate = {"session_name"})
    public String title;
    @SerializedName(value = "logo_cover", alternate = {"image_phone"})
    public String logo_cover;
    public String logo_detail;
    public int calorie;
    @SerializedName(value = "sessionCount", alternate = {"session_count"})
    public int sessionCount;
    @SerializedName(value = "peopleCount", alternate = {"downloads"})
    public int peopleCount;
    public String category_name;
    public int content_type;
    @SerializedName(value = "id", alternate = {"programId", "sessionId"})
    public int id;

总结

Q1,一个字段时而有时而没有,客户端会出现异常吗?
不会异常不会崩溃,只要不影响业务逻辑,服务端怎么简单怎么来。
Q2,一个字段返回的类型和客户端定义的类型不一致,能正常解析道数据吗?默认值分别是什么?

  • 数值类型/字符串类型对应json各种格式

      {
          "age":22,      数值类型
          "age":"22",      字符串类型
          "age":{},      布尔类型
          "age":{},      对象类型
          "age":[],      数组类型
      }
    

json数值类型和json字符串类型都能正确解析数据,其他类型会忽略解析异常,数值类型默认值0,字符串类型默认值为“”。

  • 布尔类型对应json各种格式

      {
          "isRegister":0,      数值类型
          "isRegister":false      布尔类型
          "isRegister":"false",      字符串类型布尔值
          "isRegister":"哈哈",      字符串类型非布尔值
          "isRegister":{},      对象类型
          "isRegister":[],      数组类型
      }
    

json数值类型时,0为false非0为true。json布尔类型和json字符串类型布尔值都能正确解析数据。其他类型会忽略解析异常,数值类型默认值false。

  • 对象和数组

      {
          "avatar":{},      对象类型
          "avatar":[],      数组类型
      }
    

对象类型和数组类型只有一一对应才能解析道正确数组,其他类型会忽略解析异常,对象默认值为null,数组默认值未空数组。

服务端小伙伴以后再也不用问我,字段不返回有没有什么问题,我再也不用叮嘱服务端大括号改中括号,中括号改大括号的问题了,完结撒花。
最后"null" 和 null区别,加了""就是字符串和里面包什么内容没有什么关系,null表明返回的是一个null对象,和客户端对象变量的默认值是一样的。
源码:https://github.com/YougaKing/GosnFormat.git

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

推荐阅读更多精彩内容