Python

【Python】 base64エンコードにおいてjson.dumpsでハマったこと

python記事のアイキャッチ
hama-matcha

はじめに

APIなどの作成でbase64でエンコードしたデータを返したいことありますよね?
今回はそこでハマったので書き残しです。

まとめ

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')
data_encode_bytes = base64.b64encode(data_bytes)
data_encode_str = data_encode_bytes.decode('utf-8')

この3行が成果です。

準備したデータ

PythonではDict型とJsonは同じ形をしているため,加工しやすいDict型で処理したのち最後にJsonに変換します。

akane_dict = {
            'kind': 'voiceroid',
            'data': 'アカネチャンカワイイヤッタ'
        }

いろいろ詰まってますね〜

今回は'data'をbase64でエンコードしてjson.dumpsしたいです。

dataのエンコード準備

エンコードするために'data' をbytes型にしていきます。

data_bytes = b'アカネチャンカワイイヤッタ'

akane_dict = {
            'kind': 'voiceroid',
            'data': data_bytes
        }

これはダメです。

    data = b'アカネチャンカワイイヤッタ'
          ^
SyntaxError: bytes can only contain ASCII literal characters.

'アカネチャンカワイイヤッタ'はasciiではないので当然ですね〜(1敗)

私の環境ではVSCodeの入力としてUTF-8を使用しているのでこうすればうまくいきそうですね。

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')

akane_dict = {
            'kind': 'voiceroid',
            'data': data_bytes
        }

dataのエンコード

base64は標準で入っているのでそのままimportできます。

import base64

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')
data_encode_bytes = base64.b64encode(data_bytes)

akane_dict = {
    'kind': 'voiceroid',
    'data': data_encode_bytes
}

print(akane_dict) # 確認用

Jsonへの変換

Dict型をJsonに変換していきます。

import base64
import json

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')
data_encode_bytes = base64.b64encode(data_bytes)

akane_dict = {
    'kind': 'voiceroid',
    'data': data_encode_bytes
}

akane_json = json.dumps(akane_dict)

以下のようにエラーが起こります。

TypeError: Object of type bytes is not JSON serializable

これはbytes型のままじゃJson変換できないよ〜!っと怒られてしまうのでstr型に変換する必要があります。(2敗)

作戦1

str型変えようそうだキャストだ!そうするとこうなります。

import base64
import json

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')
data_encode_bytes = base64.b64encode(data_bytes)
data_encode_str = str(data_encode_bytes)

akane_dict = {
    'kind': 'voiceroid',
    'data': data_encode_str
}

print(akane_dict)
# 送信
akane_json = json.dumps(akane_dict)

# 受信
res = json.loads(akane_json)
print(res)
res_data = base64.b64decode(res['data'])
print(res_data)
binascii.Error: Invalid base64-encoded string: number of data characters (53) cannot be 1 more than a multiple of 4 

そうですね、、str型へのキャストをするとこうなりますよね。一応resの中身見てみます。

{'kind': 'voiceroid', 'data': "b'44Ki44Kr44ON44OB44Oj44Oz44Kr44Ov44Kk44Kk44Ok44OD44K/'"}

ヤバそう、、送信はできるかもですが、dataのデコードは無理そうです。

作戦2

Python: JSON でバイナリを扱う」という記事を見つけたので参考にして以下のようにコードを変更

import base64
import json

data_bytes = bytes('アカネチャンカワイイヤッタ', 'utf-8')
data_encode_bytes = base64.b64encode(data_bytes)
data_encode_str = data_encode_bytes.decode('utf-8')

akane_dict = {
    'kind': 'voiceroid',
    'data': data_encode_str
}

print(akane_dict)
# 送信
akane_json = json.dumps(akane_dict)

# 受信
res = json.loads(akane_json)
res_data = base64.b64decode(res['data']).decode('utf-8')
print(res_data)

ここで不思議なのがbytes型をutf8でデコードして文字列にしていることです。

勉強不足でなぜこうすればいいのかは理解できてないのですが、

このようにすればうまくjson.dumpsも動いて受信側もエラーで怒らないです。

受信側ではbytes型で扱うならutf8でデコードする必要はないです。

では最後に表示して

{'kind': 'voiceroid', 'data': '44Ki44Kr44ON44OB44Oj44Oz44Kr44Ov44Kk44Kk44Ok44OD44K/'}
アカネチャンカワイイヤッタ

無事に大勝利🎉🎉🎉

さいごに

私が敗北した場所を追って解決するまでの記事を書いてみました。

結果だけほしい方はまとめを見て、サクッと活かしてもらえればOKですし、

なぜ敗北したのかなと気になる方はここまで記事を追っていただきありがとうございます。

以上hamaでした〜

_hama
_hama
ほのぼのデータエンジニア
記事URLをコピーしました