Json海量数据解析Json海量数据解析

Json海量数据解析

前言

​ 在android开发中,app和服务器进行数据传输时大多数会用到json。在解析json中通常会用到以下几种主流的解析库:jackson、gson、fastjson。而对于从server端获取的数据量很小时候,我们可能会忽略解析所产生的性能问题。而我在开发的过程中就碰到因为解析json而产生严重的问题。

问题场景

先描述以下问题的场景:app做收银库存管理。这时候每次登陆时候会去服务端同步所有的商品、分类等数据。而这时候,当商品的数量很大的时候,客户端拿到数据时候对app来说还是比较大的。而server端是将所有的数据序列化为json字符串存入到文件,然后app去下载文件并进行解析。下面说下我的修改历程。

踩坑过程

  • 第一版代码是直接讲文件读出为字符串,使用gson直接反序列化 new Gson().fromJson(String s,Type type)这时候OOM,查看日志,发现文件读出字符串时候直接OOM了(当初并没有考虑会有这么大的数据,晕倒)。从server端下载下来的文件就有20M左右。
  • 第二版代码使用FastJson的JSONReader。对每个对象进行单独序列化。也就是下面讲到的fastjson方法1。这时候OOM问题的解决了。因为是读的文件流,边读边解析数据。基本解决了问题。但通过Android Studio的Monitors发现,解析时候内存不断的在被消耗(汗。。还好没有爆掉)。
  • 第三版代码使用Fastjson的JSONReader。对每个json的每个key每个value都单独的解析和读取。也就是下面讲到的fastjson方法2。这时候所有的性能问题全部解决,速度最快,几乎没有消耗多少内存。

​ 上面是我一步步走过得坑,唉。可能对于看过fastjson源码的童鞋来说so easy。但第一次碰到后,坑还是得一步步的踩。当然也是要不断的通过看源码、写测试代码、比较内存和时间。下面是我做的一些测试。

测试验证

准备工作
  • 相关依赖库 compile group: 'com.alibaba', name: 'fastjson', version: '1.2.29' // https://mvnrepository.com/artifact/commons-io/commons-io compile group: 'commons-io', name: 'commons-io', version: '2.5' // https://mvnrepository.com/artifact/com.google.code.gson/gson compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0's
  • 测试数据生成 public void createDataFile() { List<Good> goodList = new ArrayList<>(); for (int i = 0; i < 200000; i++) { Good good = new Good(System.currentTimeMillis() + "_" + i, new String("booke") + i, 10.f + i, System.currentTimeMillis() + "", "describe book" + i, i); goodList.add(good); } try { String json = JSONArray.toJSONString(goodList); FileUtils.write(new File("e://goods.json"), json, "UTF-8"); } catch (IOException e) { log("" + e.getMessage()); e.printStackTrace(); } }
结果分析
  • gson解析 使用流进行读取。20W条数据,内存不断的被消耗。两次解析时间为 50,488ms、48,940ms 性能是相当的差
List<Good> list = new Gson().fromJson(new InputStreamReader(getAssets().open("goods.json"),     
            "UTF-8"), new TypeToken<List<Good>>() {}.getType());

1.png

  • fastjson方法1

使用流进行读取。内存也是不断被消耗。三次解析时间为 33,394ms 31,632ms 32,378ms

JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"),
            "UTF-8"));
reader.startArray();
while (reader.hasNext()) {
    Good good = reader.readObject(Good.class);
}
reader.endArray();
reader.close();
reader = null;

2.png

  • fastjson方法2 使用流进行读取,每个key和value自己来处理。三次解析时间为 31,242ms 31,583ms 30,834ms。同时,内存几乎没有太多的占用,比较的平稳。这个方法当然最优。
JSONReader reader = new JSONReader(new InputStreamReader(getAssets().open("goods.json"),
           "UTF-8"));
reader.startArray();
while (reader.hasNext()) {
    reader.startObject();
    Good good = new Good();
    while (reader.hasNext()) {
        String key = reader.readString();
        if ("id".equals(key)) {
                good.setId(reader.readString());
            } else if ("name".equals(key)) {
                good.setName(reader.readString());
            } else if ("price".equals(key)) {
                good.setPrice(Double.parseDouble(reader.readString()));
            } else if ("barCode".equals(key)) {
                 good.setBarCode(reader.readString());
            } else if ("desc".equals(key)) {
                 good.setDesc(reader.readString());
            } else if ("count".equals(key)) {
                 good.setCount(Integer.parseInt(reader.readString()));
            } else {
                 reader.readObject();
            }   
        }
    reader.endObject();
    }
reader.endArray();
reader.close();
reader = null;

3.png

最后我们对比消耗时间

5.png

其他

  • Good.java
public class Good {
  private String id;
  private String name;
  private double price;
  private String barCode;
  private String desc;
  private int count;
  public Good() {
  }
  public Good(String id, String name, double price, String barCode, String desc, int count) {
      this.id = id;
      this.name = name;
      this.price = price;
      this.barCode = barCode;
      this.desc = desc;
      this.count = count;
  }
  public String getId() {
      return id;
  }
  public void setId(String id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public double getPrice() {
      return price;
  }
  public void setPrice(double price) {
      this.price = price;
  }
  public String getBarCode() {
      return barCode;
  }
  public void setBarCode(String barCode) {
      this.barCode = barCode;
  }
  public String getDesc() {
      return desc;
  }
  public void setDesc(String desc) {
      this.desc = desc;
  }
  public int getCount() {
      return count;
  }
  public void setCount(int count) {
      this.count = count;
  }
  @Override
  public String toString() {
      return "Good{" +
              "id='" + id + '\'' +
              ", name='" + name + '\'' +
              ", price=" + price +
              ", barCode='" + barCode + '\'' +
              ", desc='" + desc + '\'' +
              ", count=" + count +
              '}';
  }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小灰灰

基于ForkJoin构建一个简单易用的并发组件

基于ForkJoin构建一个简单易用的并发组件 在实际的业务开发中,需要用到并发编程的知识,实际使用线程池来异步执行任务的场景并不是特别多,而且一般真的遇到了需...

3858
来自专栏有趣的django

PYTHON面试

大部分的面试问题,有最近要找事的老铁吗?  python语法以及其他基础部分 可变与不可变类型;  浅拷贝与深拷贝的实现方式、区别;deepcopy如果你来...

6527
来自专栏walterlv - 吕毅的博客

迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)

发布于 2017-12-18 13:41 更新于 2017-12...

1042
来自专栏Fundebug

Async/Await替代Promise的6个理由

译者按: Node.js的异步编程方式有效提高了应用性能;然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码;在实践过程中,却发...

1004
来自专栏Golang语言社区

一起用golang之Go程序的套路

系统性地介绍golang基础的资料实在太多了,这里不再一一赘述。本文的思路是从另一个角度来由浅入深地探究下Go程序的套路。毕竟纸上得来终觉浅,所以,能动手就不要...

3012
来自专栏铭毅天下

干货 | Elasticsearch Nested类型深入详解

本文通过一个例子将Nested类型适合解决的问题、应用场景、使用方法串起来, 文中所有的DSL都在Elasticsearch6.X+验证通过。

2403
来自专栏Ryan Miao

添加PMD插件扫描潜在的bug

上一节使用checkstyle来规范你的项目主要解决了代码编码规范问题,比如缩进换行等。这次继续代码健康工具类PMD。

1203
来自专栏Java3y

【Java】几道让你拿offer的面试题

之前在刷博客的时候,发现一些写得比较好的博客都会默默收藏起来。最近在查阅补漏,有的知识点比较重要的,但是在之前的博客中还没有写到,于是趁着闲整理一下。

3450
来自专栏王磊的博客

Spring Boot 最佳实践(三)模板引擎FreeMarker集成

FreeMarker是一款免费的Java模板引擎,是一种基于模板和数据生成文本(HMLT、电子邮件、配置文件、源代码等)的工具,它不是面向最终用户的,而是一款程...

2534
来自专栏web前端教室

JavaScript ES6 新特性之 Generator

(昨天晚上喝多了没更新,6瓶雪花淡爽,我就醉了~~) 今儿个学习下ES6 的生成器 Generator,这玩艺的名字挺唬人的,我刚一看的时候还以为能for循环似...

1928

扫码关注云+社区

领取腾讯云代金券