巧用JsonReader+JsonPath解析大json文本(300M+),解决Java heap space,支持根据json的key值自定义解析

Fastjson源码分析—JSONReader的使用和原理 2021SC@SDUSC 目录JSONReader使用方法JSONReader源码分析startArray方法readStringJSONReader性能测试Gson测试代码:Gson内存和cpu占用情况FastJSON测试代码FastJSON内存和cpu占用情况总结 前面几篇文章分析的都是Fastjson对于少量json字符串的反序列化。我们不需要考虑内存的消耗,也难以察觉出Fastjson性能上的优越。但是当数据量庞大时,单纯的使用JSON.parseObject会产生严重问题,最明显的就是内存溢出。 阅读详情

场景:假设我们有个文本文件内容如下,可能数据非常非常多(300M),我们要将json结构内的res数组中的数据导入数据库之中,如何解析出来

重点是每个json文件内的res数据都不一样,key值也不一样,如何写一个公用的解析方法,我们只需要指定key就能自动匹配。

{
    "status":"200",
    "res":[
        {
            "province":"山东省",
            "city":"潍坊市",
            "name":"兰州拉面馆",
            "prop":{
                "type":"餐饮单位",
                "level":1
            },
			"month_data":[
				{"month":"1","amount":18518.5},
				{"month":"2","amount":18519.5},
				{"month":"3","amount":18517.5},
				{"month":"4","amount":18516.5}
			]
        },
        {
            "province":"山东省",
            "city":"淄博市",
            "name":"电瓶批发",
            "prop":{
                "type":"其它",
                "level":2
            },
			"month_data":[
				{"month":"1","amount":28518.5},
				{"month":"2","amount":28519.5},
				{"month":"3","amount":28517.5},
				{"month":"4","amount":28516.5}
			]
        }
    ]
}

如果read出来 然后 JsonUtils 工具类转换 list<Bean> 内容一旦很多,就容易java heap space

我的办法

我采用的是com.google.gson.stream.JsonReader这个工具类是按照符号顺序读取的,不是一下加载到内存中的。

 测试入口

    public static void main(String[] args) throws Exception {
        // 创建jsonReader
        JsonReader jsonReader = new JsonReader(new FileReader(new File("C:\\Users\\Wsong\\Desktop\\test.json")));
        // 定位到指定的数组节点 如果本身就是个数组 传入$,如果本身是个对象可以传入null
        findRootArray(jsonReader, "$.res");
        // 传入jsonReader,传入key值
        ArrayList<List<String>> lists = jsonArr2list(jsonReader, Arrays.asList("$.province", "$.city", "$.name", "$.prop.type", "$.prop.level"));
        // 得到每一行的数组结果,我这里为了讲明白,没有设置数据类型 都用的string
        System.out.println(lists);
    }

结果

[[山东省, 潍坊市, 兰州拉面馆, 餐饮单位, 1, 18516.5], [山东省, 淄博市, 电瓶批发, 其它, 2, 28516.5]]

 代码

package com.ws.tools;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.ws.tools.dto.ColumnEntry;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
import java.util.*;

public class JsonReaderUtils {

    private static Gson gson = new Gson();

    public static void main(String[] args) throws Exception {
        // 创建jsonReader
        JsonReader jsonReader = new JsonReader(new FileReader(new File("C:\\Users\\Wsong\\Desktop\\test.json")));
        // 定位到指定的数组节点
        findRootArray(jsonReader, "$.res");
        // 传入jsonReader,传入key值
        ArrayList<List<String>> lists = jsonArr2list(jsonReader, Arrays.asList("$.province", "$.city", "$.name", "$.prop.type","$.month_data[3].amount"));
        // 得到每一行的数组结果,我这里为了讲明白,没有设置数据类型 都用的string
        System.out.println(lists);
    }

    public static ArrayList<List<String>> jsonArr2list(JsonReader jsonReader, List<String> jsonKeys) throws Exception {

        JsonToken peek;

        int startObj = 0;
        int endObj = 0;

        // 组装最后的结果
        ArrayList<List<String>> res = new ArrayList<>();
        StringBuilder sb = new StringBuilder();

        // 防止循环无法跳出
        int i = 0;
        while (true) {
            // 这个方法是返回json文件中下一个标签
            peek = jsonReader.peek();
            // 如果下一个标签是9 代表json文档结束 就退出循环了返回了
            if (peek.ordinal() == 9 || i > 10000000) {
                break;
            }
            // 遇到一个"{" startObj++
            if (peek.ordinal() == 2) {
                startObj++;
            }
            // 遇到一个"}" endObj++
            if (peek.ordinal() == 3) {
                endObj++;
            }
            // 读取到每个标签
            String val = peekAndRead(peek, jsonReader);

            // 对数据进行处理,看不懂可以注掉这段代码
            if (StringUtils.endsWith(sb, "{")) {
                val = StringUtils.removeStart(val, ",");
            }
            // 拼接 json
            sb.append(val);

            // 如果 遇到的 "{" 和遇到的 "}" 数量相同 说明一个对象已经读取完成,该读下一个对象了
            if (startObj == endObj && startObj != 0) {
                // 解析到的数据
                List<String> parser = parser(sb.toString(),jsonKeys);
                res.add(parser);

                sb = new StringBuilder();
                startObj = 0;
                endObj = 0;
                i = 0;
            }
            i++;
        }
        return res;
    }
    private static List<String> parser(String json,List<String> columns)
    {
        List<String> splitLine = new ArrayList<>();
        // jsonpath 解析支持  $.key $key1.$key2  $key[0].$key1
        DocumentContext document = JsonPath.parse(json);
        String tempValue;
        for (String columnIndex : columns) {
            try {
                tempValue = document.read(columnIndex,String.class);
            }
            catch (Exception ignore) {
                ignore.printStackTrace();
                tempValue = null;
            }
            splitLine.add(tempValue);
        }
        return splitLine;
    }


    private static void findRootArray(JsonReader jsonReader, String arryRootKey) throws Exception {
        // arryRootKey==$ 表明,数据根节点就是个数组,直接开启数组
        if ("$".equalsIgnoreCase(arryRootKey)) {
            jsonReader.beginArray();
        } else if (StringUtils.isBlank(arryRootKey)) {
            // 如果 arryRootKey==$ 说明是个对象,不用管
        } else {
            // 按层级查询 数组节点
            String[] keys = StringUtils.removeStart(arryRootKey, "$.").split("\\.");
            for (String s : keys) {
                JsonToken peek;
                int i = 10000;
                while (jsonReader.hasNext()) {
                    peek = jsonReader.peek();
                    if (peek.ordinal() == 4) {
                        if (jsonReader.nextName().equalsIgnoreCase(s)) {
                            jsonReader.beginArray();
                            break;
                        }
                    } else {
                        peekAndRead(peek, jsonReader);
                    }
                    i--;
                    if (i == 0) {
                        throw new Exception("json key 解析异常");
                    }
                }
            }
        }
    }

    /**
     * 将peek,转成字符 用来拼接每个对象的json
     */
    private static String peekAndRead(JsonToken p, JsonReader jsonReader) throws Exception {
        switch (p) {
            case BEGIN_OBJECT:
                jsonReader.beginObject();
                return "{";
            case END_OBJECT:
                jsonReader.endObject();
                return "}";
            case BEGIN_ARRAY:
                jsonReader.beginArray();
                return "[";
            case END_ARRAY:
                jsonReader.endArray();
                return "]";
            case NAME:
                return ",\"" + jsonReader.nextName() + "\":";
            case BOOLEAN:
                return jsonReader.nextBoolean() + "";
            case NULL:
                jsonReader.nextNull();
                return "null";
            case STRING:
            case NUMBER:
                return "\"" + jsonReader.nextString() + "\"";
            case END_DOCUMENT:
                throw new Exception("json文件解析已经完成~");
            default:
                throw new Exception("不认识的json符号 [" + p.ordinal() + "]");
        }
    }
}

Gson全解析之二:JsonReader的其它方法 上篇文章的示例代码 String json = "{\"name\":\"wisely\",\"age\":\"18\"}"; private void readJson2User(){ User user = new User(); StringReader reader = new StringReader(json); JsonReader jsonReader 阅读详情

相关推荐

SpringBoot+JaywayJsonPath实现Json数据的DSL(按照指定节点表达式解析json获取指定数据)

在上面搭建SpringBoot项目的基础上,并且在项目中引入fastjson、hutool等所需依赖后。需要对接第三方接口,接口返回的json数据需要解析获取数据。可以通过配置每个返回字段的对应json数据的表达式,使其在代码中根据配置的表达式动态获取。

BADAO_LIUMANG_QIZHI的博客 1531

Gson中JsonReader的源码解析

分享gson源码的解析

qq_32035241的博客 810

用Python实现四叉树(quad tree)

import numpy as np #正方形类 class square: #lx、ly为左上角坐标(x对应行号,y对应列号),rx、ry为右下角坐标 def __init__(self,lx,ly,rx,ry): self.lx=lx self.ly=ly self.rx=rx self.ry=ry #检查该矩阵中是否满足条件 #参数:原矩阵,阈值 #返回值:布尔类型 def check(s

weixin_44164489的博客 5868

Gson读取Json文件并解析

读取hotCitySites所有的city对象,所以还需要建一个实体类。在maven 项目下,需要导入Gson坐标。

qq_52160611的博客 1601

深入解析PVT corner:从wcl到ml的芯片设计关键考量

本文深入解析了芯片设计中的PVT corner概念,重点探讨了从传统WC到先进工艺下WCL、ML等关键工艺角。文章详细阐述了温度反转效应如何使低温WCL成为最严苛的时序场景,并提供了基于先进工艺的实战签核策略与多角多模分析流程,确保芯片在极端工艺、电压、温度下的稳健性。

tree8的博客 528

从零开始STM32cubeide学习

使用的是正点原子的精英版,单片机是STM32F103ZET6,详细讲解完成跑马灯实验

weixin_44077208的博客 2284

解析超大JSON文件

超大JSON文件导入到ES(大于10个G)如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 如何改变文本的样式 强调文本 强调文本 加粗文本 加粗文本 标记文本 ...

一只小小云 1万+

Python量化交易实战:pytdx库高效获取通达信分笔成交数据

本文详细介绍了如何使用Python的pytdx库高效获取通达信分笔成交数据,为量化交易提供关键市场微观结构分析。通过环境搭建、服务器连接、数据获取与清洗的完整流程,帮助开发者快速掌握这一免费高质量数据源,并实现分笔数据的实战分析与应用。

weixin_29061509的博客 1352

JsonReader的使用

public void test() throws IOException { String jsonString = "[{\"username\":\"xujiaowei\",\"userId\":\"28\"},{\"username\":\"Jason\",\"userId\":\"002\"}}]"; JsonReader jsonReader = new JsonReader(ne

小姓程,请叫我序员的专栏 9233

2024“华为杯”中国研究生数学建模竞赛(A题)深度剖析_数学建模完整过程+详细思路+代码全解析

目标是优化风力涡轮机的功率调度,使其在给定的实时风速数据下,最小化涡轮机关键组件(如塔架和主轴)的疲劳损伤。问题的挑战在于,考虑通信延迟和测量噪声的同时,要确保功率输出与风速变化的响应快速且准确。

2401_82549447的博客 6632

2023年全国青少年信息素养大赛复赛真题(C++智能算法复赛真题:带解析)

2023年全国青少年信息素养大赛复赛真题(C++智能算法复赛真题:带解析)

王老师青少年编程 2114

java解析超大json文件数据

此时需要使用jackson的JsonParser从文件中逐个token去读取。一般大json中都会存在某个数组中有超多的数据记录,我们需要解决的就是记录当前token路径,在读取到超大json数组时,再利用逐条数据读取mapper.readTree(jsonParser)逐条读取数据,利用数组缓存一定量的数据后,写入数据库后继续读取,知道json数组数据读取结束。这里关键的是记录当前json的token的路径,以方便地利用mapper.readTree(jsonParser)读取任意的子节点数据。

javajingling的专栏 3175

网络系统管理专栏-配套练习+知识点详解

网络系统管理专栏-配套练习+知识点详解

bfq05234214的博客 683

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

Java解析JSON大文件解决方案之JsonReader 一,使用背景 之前遇到一个需求,是需要将一个json文件解析存储到数据库中。一开始测试的时候,json文件的大小都在几兆以内,所以直接将json文件转化为字符串,再转化成JSONObject对象进行处理时不会出现问题,如下所示: File file = new File("") try(FileInputStream fileInputStream = new FileInputStream(file)) { int size = fileI

lmchhh的博客 1万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Json解析数组实例

1.     JsonCPP简介       jsoncpp是c++解析JSON串常用的解析库之一。其常用的类有: a)     Json::Value     可以表示里所有的类型,比如int,string,object,array等,其支持的类型可以参考Json:ValueType中的值。 b)     Json::Reader   将json文件流或字符串解析到Json::

nyist327的专栏 6335

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【你提我说】Python 处理超大 JSON 文件,这个方法简单

Python方面的知识、学习技巧、学习上的疑惑等.......听取小伙伴的意见,发布文章让大家学习了解! (评论或者私我就可以哦)

m0_59485658的博客 1530

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java处理超大JSON文件,避免OOM内存溢出

解析,会很容易出现耗尽内存的情况。即使原始数据大小小于内存容量,Java也会进一步增加内存使用量。这意味着程序会在与磁盘交互时处理缓慢,或在内存不足时崩溃。如果你需要在 Java 中处理一个大的 JSON 对象或 文件,直接使用。,也就是惰性解析、迭代解析或分块处理。一种常见的解决方案是。

weixin_39825906的博客 5470

Python爬虫学习笔记(五)————JsonPath解析

目录1.JSONPath —— xpath在json的应用2.JSONPath 表达式3.jsonpath的安装及使用方式4.jsonpath的使用5.JSONPath语法元素和对应XPath元素的对比6.实例 (1)商店案例(2) 解析淘票票的“城市选择”数据 pip安装: pip install jsonpath obj = json.load(open('json文件', 'r', encoding='utf‐8')) ret = jsonpath.jso

laosao_66的博客 4252

java解析大json包的两种方式

【代码】java解析大json包的两种方式。

u012374512的博客 1269

JsonReader JSONObject 区别

由于使用了GSON库解析json数据成不同的类。然后就找到了两种方法。不懂那种效率高。有时间还请高人指教。先上代码先。 首先是jsonreader的: JsonReader reader = new JsonReader(new StringReader(content)); reader.beginObject();              String tagName = reade

manyan1985的专栏 1914

超大JSON文件解析方案(Java)

超大JSON文件解析方案

淮城一只猫 5245

【第14篇】通过GSON的JsonReader去读取数据处理json数据

通过GSON的JsonReader去读取数据处理json数据

jilongliang的专栏 6985

使用Gson的JsonReader解析Json数据

原址:http://blog.youkuaiyun.com/jackie_xiaonan/article/details/15026901 自定义解析过程         这里把问题简化一下,比如获得到的json字符串是 [{"name":"Jackie","age":30,"contact":{"email":"email","phoneno":"phoneno"}},{"name":"

wuyou1336的博客 9964

jsonrreader java_利用JsonReade和JsonReader实现json解析和写数据

前面讲过调用谷歌的json包来对json数据进行解析,随着android的发展现在手机系统都是android4.0以上版本了,因此可以直接调用sdk 中的JsonReader来解析json数据了。json数据的解析成现有的类,这些类通常是javaBean类,通过查看官方的文档将其文档的例子完善了下如下:json数据:[{"id": 912345678901,"text": "How do I re...

weixin_28916013的博客 649

使用JsonReader遇到的坑(解析出错,调用顺序出现问题)

这会导致错误,因为你在读取下一个元素的名称后,没有读取该元素的任何值,而是直接再次检查是否有下一个元素。这会导致读取指针位置不正确,从而引发错误。这种调用顺序是正确的,因为你先检查是否有下一个元素,然后读取下一个元素的名称,然后读取该元素的字符串值,最后再次检查是否有下一个元素。因此,在使用 JsonReader 时,要确保按照正确的顺序调用方法,以确保正确地读取和处理 JSON 数据。

third_gold的博客 506
上一篇: spark-sql 指定metastore地址、读取mysql
下一篇: flink任务部署的方式
JNWsong
博客等级 码龄9年 287粉丝 204原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值