Android JSON解析方式分析

一、 主流解析方式对比

解析方式 使用难度 原理 优点 缺点 适用场景
Android原生 复杂 基于文档驱动 不依赖第三方库;处理速度快(不使用反射) 使用复杂;内存消耗大;兼容性差 需要解析的JSON的格式固定且数量一般
Gson 简单 基于事件驱动 使用简单;兼容性好 常规JSON数据量的项目
Fastjson 简单 基于事件驱动 特大数据量的情况下处理速度快 兼容性一般;初始化速度慢 JSON处理量特别大

二、详细说明

①Fastjson解析

技术分析

根据所需取的数据 建立1个对应于JSON数据的JavaBean类,即可通过简单操作解析出所需数据

主要缺点
  1. 数据类型兼容一般,例如对Date类型的支持
  2. 初始化速度较慢
主要优点
  1. 使用简单
适用场景
  1. JSON处理量特别大

使用示例

引入Fastjson implementation 'com.alibaba:fastjson:1.2.55'

// 读取JSON字符串(详见 三、数据准备-small.json)
String jsonStr = readJsonFile("small.json");
// 反序列化对象
DataBean data = JSON.parseObject(jsonStr,DataBean.class);
// 序列化对象
String newJsonStr = JSON.toJSONString(data);

// 读取JSON字符串(详见 三、数据准备-big.json)
jsonStr = readJsonFile("big.json");
BaseBean<List<DataBean>> newBaseBean = JSON.parseObject(jsonStr,new TypeReference<BaseBean<List<DataBean>>>(){});

复杂数据处理(泛型)

处理带有泛型的复杂数据格式时fromJson方法的第二参数应传入new TypeReference<返回的实体类型>(){}

如上代码如果fromJson方法的第二参数传入BaseBean<List<DataBean>>.class,因JAVA泛型擦除机制,泛型<List<DataBean>>是无效的,会抛出com.google.gson.JsonSyntaxException: java.lang.IllegalStateException等异常

② Gson解析

技术分析

根据所需取的数据 建立1个对应于JSON数据的JavaBean类,即可通过简单操作解析出所需数据

主要缺点
主要优点
  1. 使用简单
  2. 数据类型兼容性强.
  3. 常规数据量下速度良好
适用场景
  1. 常规JSON数据量的项目

使用示例

引入GOSN implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6'

// 读取简单JSON字符串(详见 三、数据准备-small.json)
String jsonStr = readJsonFile("small.json");
// 创建Gson对象
Gson gson = new Gson();
// 反序列化对象
DataBean data = gson.fromJson(jsonStr,DataBean.class);
// 序列化对象
String newJsonStr = gson.toJson(data);

// 读取复杂JSON字符串(详见 三、数据准备-big.json)
jsonStr = readJsonFile("big.json");
BaseBean<List<DataBean>> newBaseBean = new Gson().fromJson(jsonStr,new TypeToken<BaseBean<List<DataBean>>>(){}.getType());

复杂数据处理(泛型)

处理带有泛型的复杂数据格式时fromJson方法的第二参数应传入new TypeToken<返回的实体类型>(){}.getType()

如上代码如果fromJson方法的第二参数传入BaseBean<List<DataBean>>.class,因JAVA泛型擦除机制,泛型<List<DataBean>>是无效的,会抛出com.google.gson.JsonSyntaxException: java.lang.IllegalStateException等异常

③ Android原生解析

技术分析

解析流程:把全部文件读入到内存中 ->> 遍历所有数据 ->> 根据需要检索想要的数据

主要缺点
  1. Android原生解析没有提供直接将对象与JSON字符串互相转换的方法,需要根据实体类各字段根据数据类型逐一设置,或利用反射实现.使用复杂,兼容性差.
  2. 基于文档驱动, 不能按需读取, 解析特别大的json数据时内存消耗较大.
主要优点
  1. 不需要引入第三方库,可以缩减包的体积,降低第三方库版本的维护成本.
适用场景
  1. 项目全局需要解析的JSON格式固定且种类较少,可以自己封装相关处理方法.减少对第三方库的依赖.
  2. 需要解析的数据量较少,性能要求不高时可以考虑使用

使用示例

反序列化
// 读取JSON字符串(详见 三、数据准备-small.json)
String jsonStr = readJsonFile("small.json");
// 反序列化JSON
JSONObject jsonObject = new JSONObject(jsonStr);
// 声明反序列化结果对象
DataBean data = new DataBean();
// 获取反序列化结果的属性值并赋值
data.setBooleanData(jsonObject.getBoolean("booleanData"));
data.setByteData((byte) jsonObject.getInt("byteData"));
data.setShortData((short) jsonObject.getInt("shortData"));
data.setIntegerData(jsonObject.getInt("integerData"));
data.setLongData(jsonObject.getLong("longData"));
data.setFloatData((float) jsonObject.getDouble("floatData"));
data.setDoubleData(jsonObject.getDouble("doubleData"));
data.setStringData(jsonObject.getString("stringData"));
data.setDateData(sdf.parse(jsonObject.getString("dateData")));
JSONArray array = jsonObject.getJSONArray("arrayData");
String[] arrayData = new String[array.length()];
for (int i = 0; i < array.length(); i++) {
    arrayData[i] = array.get(i).toString();
}
data.setArrayData(arrayData);
List<String> listData = new ArrayList<>();
for (int i = 0; i < jsonObject.getJSONArray("listData").length(); i++) {
    listData.add(jsonObject.getJSONArray("listData").getString(i));
}
data.setListData(listData);
Map<String,String> mapData = new HashMap<>();
JSONObject map = jsonObject.getJSONObject("mapData");
map.names();
for (int i = 0; i < map.names().length(); i++) {
    String key =  map.names().get(i).toString();
    mapData.put(key,map.get(key).toString());
}
data.setMapData(mapData);
// 输出反序列化结果对象
...详见输出结果-反序列化得到的对象
输出结果
  • 反序列化得到的对象

    data.BooleanData: true
    data.ByteData(数字): 127
    data.ShortData: 32767
    data.IntegerData: 2147483647
    data.LongData: 9223372036854775807
    data.FloatData: 3.4028235E38
    data.DoubleData: 1.7976931348623157E308
    data.StringData: 字符串
    data.DateData: 2020-10-26
    data.ArrayDataData: ["Array元素1", "Array元素2", "Array元素3"]
    data.ListData: ["List元素1", "List元素2", "List元素3"]
    data.MapData: {"Map Key3": "Map Value3","Map Key1": "Map Value1","Map Key2": "Map Value2"}
    
序列化
JSONStringer jsonStringer = new JSONStringer();
jsonStringer.key("status").value(1);
jsonStringer.key("message").value("提示信息");
String jsonStr =  jsonStringer.toString();
// 输出序列化结果JSON字符串
输出结果
  • 序列化结果JSON字符串

    {"status":1,"message":"提示信息"}
    

代码分析

android原生解析JSON工具 包名为:org.json。这个包下主要有四个类:

  • JSONObject

    1. 该类表示了一个可更改且无序的键值对集合,简单来说直接认为这个类表示了一个 JSON 的信息。

    2. 在 JSON 字符串中,其表示了一个包裹在花括号的字符串,键和值之间使用冒号隔开,键值和键值之间使用逗号隔开的。

    3. JSONArray类中,其键名是唯一且不为null的字符串。而值则可以为 JSONObjectJSONArrayStringsBooleansIntegersLongsDouble 或者 JSONObject.NULL。特别注意,这里的NULL可不是null,而是JSONObject的一个内部类。

    4. 刚说到此类中的 NULL,它与 JAVA 中的null是不一样的,它仅仅是JSONObject中用于标识null的对象。举个例子:

      1. put(name, null) 这个方法调用将会移除该对象中对应的键值;
      2. put(name, JSONObject.NULL) 将会往对象中添加一个键值,而其值为 JSONObject.NULL
    5. 对于这个类,在使用时要要注意的就是在调用时,其会按照调用的方法进行类型转换。下面就介绍一下三类函数:

      1. getXXX() 获取一个值,此方法如果发生失败,例如没有找到对应的键值或类型转换失败,就会抛出一个JSONException异常;
      2. optXXX() 此类方法也是用于获取一个值,但是如果发生失败,其不会抛出异常,而是返回一个默认值;
      3. put()此类方法就是向对象中插入一个键值对。特别注意其插入NULLnull是不同的;
  • JSONArray

    • 该类表示了 JSON 中的值的数组,可以简单的理解其为一个普通数组,也有getXXX()optXXX()方法,但是基本都需要传入索引值。这个类代表了JSON 中的一个包裹在方括号的字符串,值和值之间使用逗号隔开的信息。
  • JSONStringer

    • 此类可以用于快速构建 JSON 字符串,它实现了JSONObject中的两个toString()方法,对于大多数程序员来说,应该直接调用JSONObjecttoString()方法而忽略这个类的:
  • JSONTokener

    • 这个类是四个类中最不常用的一个类,它能够将一个 JSON 字符串(RFC 4627)解析到相关的对象,对于大多数客户端仅仅需要使用它的构造函数和nextValue()函数:

三、数据准备

  • 简单JSON - DataBean.json

    {
      "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
      "booleanData": true,
      "byteData": 127,
      "dateData": "2020-10-26",
      "doubleData": 1.7976931348623157E308,
      "floatData": 3.4028235E38,
      "integerData": 2147483647,
      "listData": ["List元素1", "List元素2", "List元素3"],
      "longData": 9223372036854775807,
      "mapData": {
        "Map Key3": "Map Value3",
        "Map Key1": "Map Value1",
        "Map Key2": "Map Value2"
      },
      "shortData": 32767,
      "stringData": "字符串"
    }
    
  • 简单JSON对应的实体类 DataBean

    public class DataBean {
        private Boolean booleanData;
        private Byte byteData;
        private Short shortData;
        private Integer integerData;
        private Long longData;
        private Float floatData;
        private Double doubleData;
    
        private String stringData;
        private Date dateData;
    
        private String[] arrayData;
        private List<String> listData;
        private Map<String,String> mapData;
        // 省略GET SET方法....
    }
    
  • 复杂JSON - big.json

    {
      "status": "success",
      "message": "提示信息",
      "data": [{
        "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
        "booleanData": true,
        "byteData": 127,
        "dateData": "2020-10-26",
        "doubleData": 1.7976931348623157E308,
        "floatData": 3.4028235E38,
        "integerData": 2147483647,
        "listData": ["List元素1", "List元素2", "List元素3"],
        "longData": 9223372036854775807,
        "mapData": {
          "Map Key3": "Map Value3",
          "Map Key1": "Map Value1",
          "Map Key2": "Map Value2"
        },
        "shortData": 32767,
        "stringData": "字符串"
      }, {
        "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
        "booleanData": true,
        "byteData": 127,
        "dateData": "2020-10-26",
        "doubleData": 1.7976931348623157E308,
        "floatData": 3.4028235E38,
        "integerData": 2147483647,
        "listData": ["List元素1", "List元素2", "List元素3"],
        "longData": 9223372036854775807,
        "mapData": {
          "Map Key3": "Map Value3",
          "Map Key1": "Map Value1",
          "Map Key2": "Map Value2"
        },
        "shortData": 32767,
        "stringData": "字符串"
      }, {
        "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
        "booleanData": true,
        "byteData": 127,
        "dateData": "2020-10-26",
        "doubleData": 1.7976931348623157E308,
        "floatData": 3.4028235E38,
        "integerData": 2147483647,
        "listData": ["List元素1", "List元素2", "List元素3"],
        "longData": 9223372036854775807,
        "mapData": {
          "Map Key3": "Map Value3",
          "Map Key1": "Map Value1",
          "Map Key2": "Map Value2"
        },
        "shortData": 32767,
        "stringData": "字符串"
      }, {
        "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
        "booleanData": true,
        "byteData": 127,
        "dateData": "2020-10-26",
        "doubleData": 1.7976931348623157E308,
        "floatData": 3.4028235E38,
        "integerData": 2147483647,
        "listData": ["List元素1", "List元素2", "List元素3"],
        "longData": 9223372036854775807,
        "mapData": {
          "Map Key3": "Map Value3",
          "Map Key1": "Map Value1",
          "Map Key2": "Map Value2"
        },
        "shortData": 32767,
        "stringData": "字符串"
      }, {
        "arrayData": ["Array元素1", "Array元素2", "Array元素3"],
        "booleanData": true,
        "byteData": 127,
        "dateData": "2020-10-26",
        "doubleData": 1.7976931348623157E308,
        "floatData": 3.4028235E38,
        "integerData": 2147483647,
        "listData": ["List元素1", "List元素2", "List元素3"],
        "longData": 9223372036854775807,
        "mapData": {
          "Map Key3": "Map Value3",
          "Map Key1": "Map Value1",
          "Map Key2": "Map Value2"
        },
        "shortData": 32767,
        "stringData": "字符串"
      }]
    }
    
  • 复杂JSON对应的实体类 BaseBean

    public class BaseBean<T> {
        private Status status;
        private String message;
        // 文中数据 泛型T => List<DataBean> 
        private T data;
    
        public enum Status
        {
            success,
            fail
        }
        // 省略GET SET方法....
    }
    

四、速度测试

解析数据对比

统计单位: 序列化和反序列化各执行1次的平均时间(单位 ms)

解析方式 简单数据(含初始化) 复杂数据 较长数据<br />(100个简单数据的集合) 超长数据<br />(10000个简单数据的集合)
Android原生 7 11 150 2652
Gson 50 10 85 1150
Fastjson 220 20 110 1985

统计单位: 序列化和反序列化各执行10000次的平均时间(单位 ms)

解析方式 简单数据(含初始化) 复杂数据 较长数据<br />(100个简单数据的集合) 超长数据<br />(10000个简单数据的集合)
Android原生 2533 7963 159302
Gson 11188 12596 99247
Fastjson 2230 5777 98905

数据说明

  • Fastjson初始化很慢,项目中JSON处理不是特别频繁的情况下Fastjson各方面都不如Gson和Android原生处理
  • Android原生处理初始化速度最快,虽然在数据较长时表现不如Fastjson,但优化测试代码后速度应还有提升空间
  • Gson在初始化速度上不如Android原生处理但优于Fastjson,频繁处理速度远低于Fastjson和Android原生处理

测试代码

        // 读取简单JSON
        String smallStr = readJsonFile("small.json");
        // 读取复杂JSON
        String bigStr = readJsonFile("big.json");
        // 读取超长JSON
        String big100Str = readJsonFile("big100.json");
        // 读取较长JSON
        String big1000Str = readJsonFile("big10000.json");

        Map<String,String> map = new HashMap<>();
        map.put("简单数据",smallStr);
        map.put("复杂数据",bigStr);
        map.put("较长数据",big100Str);
        map.put("超长数据",big1000Str);

        // 执行次数
        int n = 1;
        // 循环统计执行时间
        for (String name:map.keySet()) {
            Log.e("TAG", "==========================" + name + "===========================");
            // 计时
            long start = System.currentTimeMillis();
            // 测试Fastjson
            for (int i = 0; i < n; i++) {
                fastjsonDemo(map.get(name),name);
            }
            Log.e("TAG", "fastjson:" + (System.currentTimeMillis() - start));

            start = System.currentTimeMillis();
            // 测试Gson
            for (int i = 0; i < n; i++) {
                gsonDemo(map.get(name),name);
            }
            Log.e("TAG", "GSON:" + (System.currentTimeMillis() - start));

            start = System.currentTimeMillis();
            // 测试原生jsonObject
            for (int i = 0; i < n; i++) {
                jsonObjectDemo(map.get(name),name);
            }
            Log.e("TAG", "jsonObject:" + (System.currentTimeMillis() - start));
        }

参考资料

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