大数据JSON流解析

大数据JSON流解析

背景

最近在做一个需求,需要每月从一个别的数据系统同步一次数据过来。数据量大概90W条左右,数据接口只提供了一个JSON接口,接口返回报文为JSON,并且没有任何分页。这个数据量如果直接使用普通方式解析的话,肯定内存溢出。

解决思路

我们要保证内存溢出,那么就不能把得的数据全部存放在内存然后处理。通常我们在处理一些大的数据文件时也会有同样的情况,我们可能会在读取文件的流中一行一行的对数据进行处理,处理完的数据丢弃,将会被垃圾回收,这样一个很大的文件也可以保证正常处理。

那么对于接口,实际响应报文也是一个数据流。我们是否可以边获取数据流,别解析JSON呢?

FastJson:JSONReader

  1. 我们首先看这个Reader的构造方法,正好是传入一个流,正好符合我们要求。

    public class JSONReader implements Closeable {
    
        private final DefaultJSONParser parser;
        private JSONStreamContext       context;
    
        //注意这个构造方法,传入的数据对象是一个字符流
        public JSONReader(Reader reader){
            this(reader, new Feature[0]);
        }
    
        .....
    }
  2. 数据读取,有多个类似readString这样的方法去读取数据。

    //读取一个String的数据
    public String readString() {
        Object object;
        if (context == null) {
            object = parser.parse();
        } else {
            readBefore();
            JSONLexer lexer = parser.lexer;
            if (context.state == JSONStreamContext.StartObject && lexer.token() == JSONToken.IDENTIFIER) {
                object = lexer.stringVal();
                lexer.nextToken();
            } else {
                object = parser.parse();
            }
            readAfter();
        }
    
        return TypeUtils.castToString(object);
    }
  3. 如何使用
    假设我们有这样一个json的流
{
    "result":[
        {
            "name":"张三",
            "age":20,
            "amt":10129.06
        },
        {
            "name":"李四",
            "age":20,
            "amt":10129.06
        }
    ],
    "status":"success",
    "message":"操作成功"
}

那么我们可以这么解析

String jsonStr = "{\"result\":[{\"name\":\"张三\",\"age\":20,\"amt\":10129.06},{\"name\":\"李四\",\"age\":20,\"amt\":10129.06}],\"status\":\"success\",\"message\":\"操作成功\"}";
StringReader stringReader = new StringReader(jsonStr);
JSONReader jsonReader = new JSONReader(stringReader);
//相当于开始读整个json的Object对象。
jsonReader.startObject();
while (jsonReader.hasNext()) {
    String elem = jsonReader.readString();
    System.out.println(elem);
    //这么判断是为了防止对象顺序会乱,如果result,status等的顺序固定不需要判断
    if ("result".equals(elem)) {
        jsonReader.startArray();
        while (jsonReader.hasNext()) {
            jsonReader.startObject();
            while (jsonReader.hasNext()) {
                //这里我把所有value按照Object来统一处理.当然也可以根据实际类型调用其他方法
                String itemKey = jsonReader.readString();
                System.out.println(itemKey);
                Object o = jsonReader.readObject();
                String itemValue = null;
                if (o != null) {
                    itemValue = String.valueOf(o);
                }
                System.out.println(itemValue);
            }
            jsonReader.endObject();
        }
        jsonReader.endArray();
    } else if ("status".equals(elem)) {
        String s = jsonReader.readString();
        System.out.println(s);
    } else {
        //不需要的数据,也必须读,可以不做处理
        jsonReader.readString();
    }
}
jsonReader.endObject();

输出结果

result
name
张三
age
20
amt
10129.06
name
李四
age
20
amt
10129.06
status
success
message

数据从接口获得

下面就是使用接口访问时,流解析的过程。一边解析一边处理就可以保证数据用完被垃圾回收。

private int readFromUrl(String url,String month) {
   try {
       logger.info("请求地址:" + url);
       URL destURL = new URL(url);

       HttpURLConnection urlConn = (HttpURLConnection) destURL.openConnection();
       urlConn.setRequestProperty("Content-Type",
               "text/x-www-form-urlencoded; charset=utf-8");
       urlConn.setDoOutput(true);
       urlConn.setDoInput(true);
       urlConn.setConnectTimeout(300000);//300秒连接时间 
       urlConn.setReadTimeout(3000000);//3000秒读取时间,数据量大可以设置长些
       urlConn.setAllowUserInteraction(false);
       urlConn.setUseCaches(false);
       urlConn.setRequestMethod("GET");

       int responseCode = urlConn.getResponseCode();
       if (responseCode != 200) {
           logger.error("请求失败,responseCode:" + responseCode);
           throw new RuntimeException("请求失败,responseCode:" + responseCode);
       }
       //开始解析数据流
       BufferedInputStream is = new BufferedInputStream(urlConn.getInputStream());
       BufferedReader br = new BufferedReader(new InputStreamReader(is,"utf-8"));
       JSONReader jsonReader = new JSONReader(br);
       //解析并保存。这里就可以边解析边处理了。
       int saveCount = parseJsonAndSave(jsonReader, month);
       is.close();
       br.close();
       jsonReader.close();
       return saveCount;
   } catch (Exception e) {
       logger.error("请求数据同步接口失败:",e);
       throw new RuntimeException("请求数据同步接口失败",e);
   }
}

总结

JSONReader实现基本就是按照文本顺序往下读,比如startObject马上读取是否是”{“。这样将一个整体的大块数据,变成了一个个字符读取,除了解决了内存问题,其实用它解析json效率也是比一般方法高很多。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值