大数据JSON流解析(转)
大数据JSON流解析
背景
最近在做一个需求,需要每月从一个别的数据系统同步一次数据过来。数据量大概90W条左右,数据接口只提供了一个JSON接口,接口返回报文为JSON,并且没有任何分页。这个数据量如果直接使用普通方式解析的话,肯定内存溢出。
解决思路
我们要保证内存溢出,那么就不能把得的数据全部存放在内存然后处理。通常我们在处理一些大的数据文件时也会有同样的情况,我们可能会在读取文件的流中一行一行的对数据进行处理,处理完的数据丢弃,将会被垃圾回收,这样一个很大的文件也可以保证正常处理。
那么对于接口,实际响应报文也是一个数据流。我们是否可以边获取数据流,别解析JSON呢?
FastJson:JSONReader我们首先看这个Reader的构造方法,正好是传入一个流,正好符合我们要求。publicclass JSONReader implements Closeable {
-
private final DefaultJSONParser parser; private JSONStreamContext context; //注意这个构造方法,传入的数据对象是一个字符流 public JSONReader(Reader reader){ this(reader, new Feature[0]); } ..... }
-
数据读取,有多个类似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效率也是比一般方法高很多。
转载:大数据JSON流解析 https://blog.csdn.net/xbliu564/article/details/78944755