壓縮 json 的一些嘗試

weixin_33859844發表於2017-06-08

前言

因為自己維護著一款課程表軟體,軟體裡有通過二維碼分享課程的功能,大致實現就是將課程資訊轉換為 json 放入二維碼中,掃描後解析 json 還原課程資訊。

但有時課程表內有太多課程,導致 json 過長,生成的二維碼過於密集,不好掃描,於是便有了這次壓縮 json 的嘗試。

縮短 Key

json 是 key-value 結構,如果定義好規範,則可以將 key 儘量縮短,甚至是無意義的字母,但前提是文件一定要寫清楚,避免不必要的麻煩。

我在專案中使用了 abcd... 來代替 key,縮短了一半的長度:

[
 {"h":"華羅庚樓","g":"數學","f":0,"e":2,"b":0,"d":0,"c":1,"a":1},
 {"h":"華盛頓樓","g":"英語","f":0,"e":15,"b":1,"d":0,"c":0,"a":3},
 {"h":"聞一多樓","g":"語文","f":0,"e":13,"b":1,"d":0,"c":0,"a":1},
 {"h":"居里夫人樓","g":"化學","f":0,"e":6,"b":0,"d":0,"c":0,"a":3},
 {"h":"華羅庚樓","g":"數學","f":0,"e":2,"b":0,"d":0,"c":1,"a":4},
 {"h":"聞一多樓","g":"語文","f":0,"e":13,"b":0,"d":0,"c":0,"a":0},
 {"h":"伽利略樓","g":"物理","f":0,"e":19,"b":0,"d":0,"c":1,"a":2},
 {"h":"伽利略樓","g":"物理","f":0,"e":19,"b":1,"d":0,"c":0,"a":0}
]

如果使用的是 org.json.JSONObject,直接放入鍵值對就行,比較方便。
而我的專案中使用的是 Gson 2.8.0 版本,想要實現需要額外的程式碼。

首先在屬性上加上註解,舉個例子:

@Expose //沒有此註解的屬性不會被序列化
@SerializedName("a") //序列化後的名稱
public int week;

然後建立 Gson 物件並序列化:

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Type type = new TypeToken<List<ClassBean>>() {}.getType();
return gson.fromJson(json, type);

這裡舉例的 type 為 List<ClassBean>,請根據實際情況替換。
這樣就能得到和上邊一樣的 json 了。

Deflater & Inflater

Deflater 是同時使用了LZ77演算法與哈夫曼編碼的一個無損資料壓縮演算法。

1978808-8bd96e9ced317c58.jpg
LZ77演算法圖解(轉載自Bugly)

我們可以使用 java 提供的 Deflater 和 Inflater 類對 json 進行壓縮和解壓縮:

// 壓縮
public static String zipString(String unzip) {
    Deflater deflater = new Deflater(9); // 0 ~ 9 壓縮等級 低到高
    deflater.setInput(unzip.getBytes());
    deflater.finish();

    final byte[] bytes = new byte[256];
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);

    while (!deflater.finished()) {
        int length = deflater.deflate(bytes);
        outputStream.write(bytes, 0, length);
    }

    deflater.end();
    return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_PADDING);
}
// 解壓縮
@Nullable
public static String unzipString(String zip) {
    byte[] decode = Base64.decode(zip, Base64.NO_PADDING);

    Inflater inflater = new Inflater();
    inflater.setInput(decode);

    final byte[] bytes = new byte[256];
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);

    try {
        while (!inflater.finished()) {
            int length = inflater.inflate(bytes);
            outputStream.write(bytes, 0, length);
        }
    } catch (DataFormatException e) {
        e.printStackTrace();
        return null;
    } finally {
        inflater.end();
    }

    return outputStream.toString();
}

壓縮過後的 json 又縮小了 44%:

eNqLrlbKULJSetrb93zv9Ke7Zj1bukdJRykdKPRs6oana5cBOWlKVgY6SqlKVkY6SklgdgqYTFayMtRRSgSSt
ToIU2bPfrlwP8KUF90bX6xfi2yKoSnYGEMkYwzAxhjDjHk5ffeTHQ1PlyA5BmjGs2ntKMYY4zAG4ZqNrS/be5
4uWf1k1y6ESU97pqF5ywzDW2juoShwTMjxFaZjDGDGPNmz92nHyudTlyKMed658vmENhRjLHG4xogcYwyxuCY
WANlUxMk

二維碼對比

1978808-44b2c830231593b4.jpg
壓縮前
1978808-ee5436c1bca8f950.jpg
壓縮後

可以看到二維碼的內容已經少了很多,這次的優化就到此為止了。
歡迎留言探討,謝謝大家。

相關文章