Java解析JSON大文件解决方案之JsonReader

一,使用背景

之前遇到一个需求,是需要将一个json文件解析存储到数据库中。一开始测试的时候,json文件的大小都在几兆以内,所以直接将json文件转化为字符串,再转化成JSONObject对象进行处理时不会出现问题,如下所示:

File file = new File("")
try(FileInputStream fileInputStream = new FileInputStream(file)) {int size = fileInputStream.available();byte[] buffer = new byte[size];fileInputStream.read(buffer);String jsonString = new String(buffer, StandardCharsets.UTF_8);jsonString.replaceAll("\n", "");jsonString.replaceAll("\r", "");JSONObject json = JSON.parseObject(jsonString);
}

但是,当出现几十兆文件的时候,这时候就会报出内存溢出的错误

java.lang.OutOfMemoryError: Java heap space

虽然稍微大一点的文件,可以通过调整JVM参数来解决,如下所示

-Xms512m -Xmx2048m

但是这毕竟不是最合理的方法,因为当文件大到一定程度后,字节数组和字符串类型都存在接收不了的情况。因此,只能选择另外的方式,此时,Google的JsonReader是一个不错的解决方案。

二,JsonReader的使用

maven依赖如下:

<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version>
</dependency>

JsonReader读取 JSON (RFC 7159) 编码值作为令牌流。 此流包括文字 值(字符串、数字、布尔值和空值)以及开始和 对象和数组的结束分隔符。 令牌被遍历 深度优先顺序,与它们在 JSON 文档中出现的顺序相同。 在 JSON 对象中,名称/值对由单个标记表示。

解析json

创建递归下降解析器 JSON ,首先创建 创建一个入口点方法 JsonReader.

每个对象类型和每个数组类型都需要一个方法。

  • 数组处理 方法中,首先调用 beginArray()消耗数组的左括号。 然后创建一个累积值的while循环,在何时终止 hasNext()为false。 最后,通过调用读取数组的右括号 endArray()
  • 对象处理 方法中,首先调用 beginObject()消耗对象的左大括号。 然后创建一个while循环根据局部变量的名称为其赋值。 这个循环应该在什么时候终止 hasNext()为false。 最后,通过调用读取对象的右括号 endObject().

当遇到嵌套对象或数组时,委托给对应的处理方法。

当遇到未知名称时,严格的解析器应该失败并返回。 但宽松的解析器应该调用 skipValue()递归地 跳过值的嵌套标记,否则可能会发生冲突。

如果一个值可能为空,应该首先检查使用 peek(). 空字面量可以使用 nextNull()或者 skipValue().

例如,我之前要解析的json文件格式如下:

{"INFO": {"NAME": "","Result": "","Config": "",...},"ATTR": {"key01": "val01","key02": "val02",...},"Parms": [{"k": "","v": "","p": "","m": "","l": ""},{"k": "","v": "","p": "","m": "","l": ""},...],"List": ["xxx", "xxxx", ...]
}

那按照JsonReader解析的思路,我应该先消费整体对象的{,再逐个对INFO,ATTR,Parms,List进行处理,总而言之,就是

String fileName = "";
FileReader in = new FileReader(fileName);
JsonReader reader = new JsonReader(in);
reader.beginObject();
String rootName = null;
while (reader.hasNext()) {rootName = reader.nextName();if("INFO".equals(rootName)) {reader.beginObject();while (reader.hasNext()) {System.out.println(reader.nextName() + ":" + reader.nextString())}reader.endObject();}else if("ATTR".equals(rootName)) {reader.beginObject();while (reader.hasNext()) {System.out.println(reader.nextName() + ":" + reader.nextString())}reader.endObject();}else if("Parms".equals(rootName)) {reader.beginArray();while (reader.hasNext()) {reader.beginObject();String k = null;while (reader.hasNext()) {k = reader.nextName();switch (k) {case "k":xxx;break;case "v":xxx;break;case "p":xxx;break;case "m":xxx;break;case "l":xxx;break;default:reader.nextString();break;}}reader.endObject();}reader.endArray();}else if("List".equals(rootName)) {reader.beginArray();while (reader.hasNext()) {System.out.println(reader.nextString());}reader.endArray();}else {reader.skipValue();}
}

常用方法如下所示:

方法名 返回值 描述
beginArray() void 使用JSON流中的下一个令牌,并断言它是新数组的开始。
endArray() void 使用JSON流中的下一个令牌,并断言它是当前数组的结尾。
beginObject() void 使用JSON流中的下一个令牌,并断言它是新对象的开始。
endObject() void 使用JSON流中的下一个令牌,并断言它是当前对象的结尾。
close() void 关闭此 JSON阅读器 和底层 Reader.
getPath() String 返回JSON值中当前位置的JsonPath。
hasNext() Boolean 如果当前数组或对象有其他元素,则返回true。
isLenient() Boolean 如果此解析器在接受的内容上是宽松的,则返回true。
setLenient(boolean lenient) void 将此解析器配置为在其接受的内容上宽松。
nextBoolean() boolean 返回boolean下一个令牌的值,并使用它。
nextDouble() double 返回double下一个令牌的值,并使用它。
nextInt() int 返回int下一个令牌的值,并使用它。
nextLong() long 返回long下一个令牌的值,并使用它。
nextName() String 返回下一个标记,即属性名,并使用它。
nextNull() void 使用JSON流中的下一个令牌,并断言它是文本null。
nextString() String 返回使用下一个标记的字符串值。
peek() JsonToken 返回下一个令牌的类型,而不使用它
skipValue() void 递归跳过下一个值。

通过使用JsonReader,现在我解析几十兆的文件基本没有问题(上百兆的还没尝试过),一个44.5M的JSON文件在4秒就能够处理完。

Java解析JSON大文件解决方案之JsonReader相关推荐

  1. 如何使用java解析json文件并将其写入数据库

    JAVA解析JSON数据文件 在使用第三方的api文档时,会得到相应的JSON数据文件,那么我们怎样将JSON文件写入数据库从而测试数据呢?下面我将给大家做一个简单的展示. 一.什么是JSON JSO ...

  2. Python 获取接口数据,解析JSON,写入文件

    Python 获取接口数据,解析JSON,写入文件 用于练手的例子,从国家气象局接口上获取JSON数据,将它写入文件中,并解析JSON: 总的来说,在代码量上,python代码量要比java少很多.而 ...

  3. Java高效读取大文件(转)

    Java高效读取大文件 1.概述 本教程将演示如何用Java高效地读取大文件.这篇文章是Baeldung(http://www.baeldung.com/) 上"Java--回归基础&quo ...

  4. Java解析JSON出现双引号变成转义字符quot;解决办法

    Java解析JSON出现双引号变成转义字符" 问题描述: 在接口中读取传过来的json数据,但是双引号都转义成了" 读取xml文件格式如下: {"message" ...

  5. asp.net core mvc上传大文件解决方案

    asp.net core mvc上传大文件解决方案 参考文章: (1)asp.net core mvc上传大文件解决方案 (2)https://www.cnblogs.com/eggtwo/p/988 ...

  6. JavaScript 下载大文件解决方案(Blob+OjbectURL)

    JavaScript 下载大文件解决方案(Blob+OjbectURL) 参考文章: (1)JavaScript 下载大文件解决方案(Blob+OjbectURL) (2)https://www.cn ...

  7. Php流式 大文件,如何使用PHP解析XML大文件

    如果使用 PHP 解析 XML 的话,那么常见的选择有如下几种:DOM.SimpleXML.XMLReader.如果要解析 XML 大文件的话,那么首先要排除的是 DOM,因为使用 DOM 的话,需要 ...

  8. java解析json数组

    java解析json数组 import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; p ...

  9. java解析json的一种方法

    package com.hanchao.web; import net.sf.json.JSONArray; import net.sf.json.JSONObject; /************* ...

  10. Java解析JSON格式数据

    Java解析JSON格式的数据主要用到两个类JSONObject和JSONArray,这两个类在json-lib-2.4-jdk15包中,在使用JSONObject时除了需要这个包外还需要相关的依赖包 ...

最新文章

  1. java2018笔试基础题_java基础笔试题
  2. Maya人物角色行走动画制作视频教程
  3. 编程开发之--单例模式(6)单元测试
  4. 计算机专业哪家强,计算机专业哪家强?这4所大学水平一流,网友:都是“大佬”级别...
  5. 移动办公计算机,最适合移动办公的三款掌上电脑点评
  6. linux编译器项目,编译器架构 LLVM
  7. java定时任务_ftp上传软件,ftp上传软件定时功能教程
  8. iOS 计步器的几种实现方式
  9. 【现代机器人学】名词概念的理解
  10. mysql执行程序_Sql在Mysql的执行
  11. error: ‘avcodec_alloc_frame’ was not declared in this scope
  12. 获取url路径上的参数,(避免中文乱码)
  13. 如何用php代码实现人脸识别,PHP实现人脸识别技术
  14. svm对未知数据的分类_SVM多分类之一对一与一对多
  15. SVM多分类器算法-一对多
  16. 计算机怎么演示音乐,做ppt的时候怎样添加音乐 想要在PPT里面增添视频以及音乐怎么操作...
  17. FinalShell SSH工具安装步骤及介绍
  18. 《编程之美》读书笔记(三):烙饼问题与搜索树
  19. React Native Text 组件显示不全解决方案
  20. sunny-ngrok linux命令,ngrok使用

热门文章

  1. Httpclient4 简介
  2. 年会 炫酷 抽奖小程序
  3. 天宫初级认证答案_百度初级认证考试题(附答案)
  4. web学习6-jdom
  5. 苹果iOS申请个人开发者账号注册教程
  6. jq实现点击复制文本功能
  7. 学习周刊-2021年第19周
  8. android应用的关闭自动更新,软件自动更新怎么关闭 安卓关闭软件自动更新
  9. 微信公众号开发(个人订阅号)
  10. 图的遍历:深度优先遍历(DFS)