追記: 最近はMessagePackを派生したCBORがRFCとして標準化されているようです
こういう記事を読んだ。
URLにデータを載っける発想は面白い。せっかくなのでMessagePackにしたついでにzstdとかで圧縮してからBase64エンコードしたほうが小さくなるのではないかと思った。
また、最初からJSONを圧縮したほうが小さくなる可能性もあるので、つまり以下のパターンを考えられる。
- JSONをBase64エンコードする
- JSONをMessagePackに変換してからBase64エンコードする
- JSONをzstdなどで圧縮してからBase64エンコードする
- JSONをMessagePackに変換してからzstdなどで圧縮してからBase64エンコードする
せっかくなのでマトリックステストしよう。
元データ
元記事からデータを拝借する。実験するときには忘れてたけど、実際はコンパクトな形式で運用する。またtrailing commaは実際は取り除く。
[ {"price":100,"label":"","createdAt":1681602213424}, {"price":100,"label":"","createdAt":1681602213424}, {"price":100,"label":"","createdAt":1681602213424}, ]
echo "$JSON" | wc -c 166 echo "$JSON" | jq -c | wc -c 155
テストする
JSONからMessagePackに変換する便利ツールとしてmsgpack-toolsというコマンドラインツールがあるのでこれを使う。
先に結果のプロットだけ示すとこうなった。
考察
MessagePackを経由しなくてもJSONを圧縮するだけで十分な効果が得られるが、MessagePackを通すと確実にさらに小さくできることがわかった。
ブラウザ上実行が可能な圧縮方法については詳しくないが、まあだいたいのアルゴリズムはやろうと思えばブラウザ上で動作させられるし、なんならRustからWASMを経由して高速圧縮といった芸当も可能だと思う。
元記事のデータの性質上、繰り返し構造がよく出現するので圧縮アルゴリズムの得意分野にヒットしたのかもしれない。
走らせたコード
以下のテストコードを走らせてサイズがどうなるか確認した。
#!/bin/sh JSON='[ {"price":100,"label":"","createdAt":1681602213424}, {"price":100,"label":"","createdAt":1681602213424}, {"price":100,"label":"","createdAt":1681602213424} ] ' echo "# raw" echo "$JSON" | jq -c echo "$JSON" | jq -c | wc -c echo "# base64" # -w 0を使い、改行を阻止する echo "$JSON" | base64 -w 0 echo echo "$JSON" | base64 -w 0 | wc -c echo "# mp + b64" echo "$JSON" | json2msgpack | base64 -w 0 echo echo "$JSON" | json2msgpack | base64 -w 0 | wc -c echo "# zstd + b64" echo "$JSON" | zstd | base64 -w 0 echo echo "$JSON" | zstd | base64 -w 0 | wc -c echo "# gz + b64" echo "$JSON" | gzip | base64 -w 0 echo echo "$JSON" | gzip | base64 -w 0 | wc -c echo "# deflate + b64" echo "$JSON" | openssl zlib | base64 -w 0 echo echo "$JSON" | openssl zlib | base64 -w 0 | wc -c echo "# brotli + b64" echo "$JSON" | brotli | base64 -w 0 echo echo "$JSON" | brotli | base64 -w 0 | wc -c echo "# mp + zstd + b64" echo "$JSON" | json2msgpack | zstd | base64 -w 0 echo echo "$JSON" | json2msgpack | zstd | base64 -w 0 | wc -c echo "# mp + gz + b64" echo "$JSON" | json2msgpack | gzip | base64 -w 0 echo echo "$JSON" | json2msgpack | gzip | base64 -w 0 | wc -c echo "# mp + deflate + b64" echo "$JSON" | json2msgpack | openssl zlib | base64 -w 0 echo echo "$JSON" | json2msgpack | openssl zlib | base64 -w 0 | wc -c echo "# mp + brotli + b64" echo "$JSON" | json2msgpack | brotli | base64 -w 0 echo echo "$JSON" | json2msgpack | brotli | base64 -w 0 | wc -c
以下のような結果になった。
# raw [{"price":100,"label":"","createdAt":1681602213424},{"price":100,"label":"","createdAt":1681602213424},{"price":100,"label":"","createdAt":1681602213424}] 155 # base64 WwogIHsicHJpY2UiOjEwMCwibGFiZWwiOiIiLCJjcmVhdGVkQXQiOjE2ODE2MDIyMTM0MjR9LAogIHsicHJpY2UiOjEwMCwibGFiZWwiOiIiLCJjcmVhdGVkQXQiOjE2ODE2MDIyMTM0MjR9LAogIHsicHJpY2UiOjEwMCwibGFiZWwiOiIiLCJjcmVhdGVkQXQiOjE2ODE2MDIyMTM0MjR9Cl0KCg== 224 # mp + b64 k4OlcHJpY2VkpWxhYmVsoKljcmVhdGVkQXTPAAABh4dNdjCDpXByaWNlZKVsYWJlbKCpY3JlYXRlZEF0zwAAAYeHTXYwg6VwcmljZWSlbGFiZWygqWNyZWF0ZWRBdM8AAAGHh012MA== 140 # zstd + b64 KLUv/QRYHQIAtANbCiAgeyJwcmljZSI6MTAwLCJsYWJlbCI6IiIsImNyZWF0ZWRBdCI6MTY4MTYwMjIxMzQyNH0sCl0KCgEAh3KqoDXhgVw= 108 # gz + b64 H4sIAAAAAAAAA4vmUlCoViooykxOVbIyNDDQUcpJTErNUbJSUtJRSi5KTSxJTXEsAUqZWRiaGRgZGRqbGJnU6tBRF1csFxcAUxgViKYAAAA= 108 # deflate + b64 eJyL5lJQqFYqKMpMTlWyMjQw0FHKSUxKzVGyUlLSUUouSk0sSU1xLAFKmVkYmhkYGRkamxiZ1OrQURdXLBcXAH1mK5I= 92 # brotli + b64 H6UAwI3UYs0Z7qRz6y+JPRM4Mma1ZGdpNmfotCiz1BKhIpm0YfYYmIpwviHpFKesS/VUR6ZGZX234AA= 80 # mp + zstd + b64 KLUv/QRYXQEANAKTg6VwcmljZWSlbGFiZWygqWNyZWF0ZWRBdM8AAAGHh012MAEAi4KqJ4ccduU= 76 # mp + gz + b64 H4sIAAAAAAAAA5vcvLSgKDM5NWVpTmJSas6ClclFqYklqSmOJecZGBjb233LDKihAgC9FD/9ZwAAAA== 80 # mp + deflate + b64 eJyb3Ly0oCgzOTVlaU5iUmrOgpXJRamJJakpjiXnGRgY29t9ywyooQIA3fkqVQ== 64 # mp + brotli + b64 H2YAaEYn99ACbZ5YgC2QoHkgSZBRPUklmxylPqM6N2G7nYNz8QeoSoMF 56