大數據JSON流解析(轉)


大數據JSON流解析

背景

最近在做一個需求,需要每月從一個別的數據系統同步一次數據過來。數據量大概90W條左右,數據接口只提供了一個JSON接口,接口返回報文為JSON,並且沒有任何分頁。這個數據量如果直接使用普通方式解析的話,肯定內存溢出。

解決思路

我們要保證內存溢出,那么就不能把得的數據全部存放在內存然后處理。通常我們在處理一些大的數據文件時也會有同樣的情況,我們可能會在讀取文件的流中一行一行的對數據進行處理,處理完的數據丟棄,將會被垃圾回收,這樣一個很大的文件也可以保證正常處理。

那么對於接口,實際響應報文也是一個數據流。我們是否可以邊獲取數據流,別解析JSON呢?

FastJson:JSONReader我們首先看這個Reader的構造方法,正好是傳入一個流,正好符合我們要求。publicclass JSONReader implements Closeable {

  1. 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. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM