Java实现解析JSON大文件JsonReader工具详解

 更新时间:2023年01月11日 09:59:00   作者:李奈 - Leemon  
这篇文章主要介绍了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工具详解的文章就介绍到这了,更多相关Java JsonReader内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用JPA主键@Id,@IdClass,@Embeddable,@EmbeddedId问题

    使用JPA主键@Id,@IdClass,@Embeddable,@EmbeddedId问题

    这篇文章主要介绍了使用JPA主键@Id,@IdClass,@Embeddable,@EmbeddedId问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 值得Java开发者关注的7款新工具

    值得Java开发者关注的7款新工具

    作为老牌语言Java,其生态圈也出来了一些有关云服务、监控、文档分享方面的工具,这篇文章主要介绍了Java开发者值得关注的7款新工具,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • springboot如何读取自定义属性

    springboot如何读取自定义属性

    大家好,本篇文章主要讲的是springboot如何读取自定义属性,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • 亲身体验Intellij Idea从卡顿到顺畅全过程

    亲身体验Intellij Idea从卡顿到顺畅全过程

    这篇文章主要介绍了亲身体验Intellij Idea从卡顿到顺畅全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • springboot结合前端实现网页跳转功能实例

    springboot结合前端实现网页跳转功能实例

    今天处理Springboot统一异常拦截的时候,遇到了页面跳转的问题,这篇文章主要给大家介绍了关于springboot结合前端实现网页跳转功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • 关于logback日志级别动态切换的四种方式

    关于logback日志级别动态切换的四种方式

    这篇文章主要介绍了关于logback日志级别动态切换的四种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 教你在Spring Boot微服务中集成gRPC通讯的方法

    教你在Spring Boot微服务中集成gRPC通讯的方法

    这篇文章主要介绍了教你在Spring Boot微服务中集成gRPC通讯的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • Springboot常用注解及作用说明

    Springboot常用注解及作用说明

    这篇文章主要介绍了Springboot常用注解及作用说明,Springboot开发中注解是非常重要的不可或缺的,那么Springboot中有哪些常用的注解呢,今天我们就来看一下这些注解和其作用,需要的朋友可以参考下
    2023-08-08
  • Java中ScheduledExecutorService介绍和使用案例(推荐)

    Java中ScheduledExecutorService介绍和使用案例(推荐)

    ScheduledExecutorService是Java并发包中的接口,用于安排任务在给定延迟后运行或定期执行,它继承自ExecutorService,具有线程池特性,可复用线程,提高效率,本文主要介绍java中的ScheduledExecutorService介绍和使用案例,感兴趣的朋友一起看看吧
    2024-10-10
  • SpringBoot+MyBatis简单数据访问应用的实例代码

    SpringBoot+MyBatis简单数据访问应用的实例代码

    这篇文章主要介绍了SpringBoot+MyBatis简单数据访问应用的实例代码,需要的朋友可以参考下
    2017-05-05

最新评论