栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

问题:假设我们有个文本文件内容如下,可能数据非常非常多(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 内容一旦很多,就容易java help 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> 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> lists = jsonArr2list(jsonReader, Arrays.asList("$.province", "$.city", "$.name", "$.prop.type","$.month_data[3].amount"));
        // 得到每一行的数组结果,我这里为了讲明白,没有设置数据类型 都用的string
        System.out.println(lists);
    }

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

        JsonToken peek;

        int startObj = 0;
        int endObj = 0;

        // 组装最后的结果
        ArrayList> 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 parser = parser(sb.toString(),jsonKeys);
                res.add(parser);

                sb = new StringBuilder();
                startObj = 0;
                endObj = 0;
                i = 0;
            }
            i++;
        }
        return res;
    }
    private static List parser(String json,List columns)
    {
        List 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 解析异常");
                    }
                }
            }
        }
    }

    
    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() + "]");
        }
    }
}

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1039300.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号