Pythonでjson dumpsを使いこなそう!(encoding、foramt、datetime)

みなさんこんにちは!

JSONファイルは正しく扱えていますか?PythonでJSONを出力の際、文字化けしてしまったり、datetime型がうまく扱えずにエラーがでてしまったり、エンコード問題に悩まされたりしていませんか?

今日はそんな方たちのために、JSONのdumpsについて詳しく見ていきたいと思います!

目次

json dumpsとは

さて、実際にdumps関数をのどのように使うのか見ていまえに、そもそもdumps関数がどんなものかということについて見ていきます。

dumps関数とはデータをJSON形式にエンコードすることのできる関数です。jsonライブラリでは、loads関数がデコード、dumps関数がエンコードとなっています。

エンコードとはデータを別の型に変換してくれることで、デコードとはエンコードされた型をもとに戻すことです。

loads関数や、そもそもJSONが何か怪しいという方は以下の記事で詳しく説明しているので見てみてください。

json dumpsを使ってみよう!

さて、それでは実際にどのように使うのかみていきましょう。

Pythonでjsonファイルを扱うには標準モジュールであるjsonをインポートする必要があるので以下の一文を忘れないようにしましょう。

import json

実際のコードを見ていきましょう。

import json

dict = {"name": "tarou", "age": 23, "gender": "man"}

enc = json.dumps(dict)

print(dict)
print(type(dict))
print("******************")
print(enc)
print(type(enc))
{'name': 'tarou', 'age': 23, 'gender': 'man'}
<class 'dict'>
******************
{"name": "tarou", "age": 23, "gender": "man"}
<class 'str'>

このように、ディクショナリ型をエンコードしたことによって文字列に変わったことがわかりますね。上で紹介したloads関数はこの逆で文字列として受け取ったものをディクショナリ型で返してくれます。

つまり、JSONのオブジェクトとして受け取るための関数がloads関数ということですね。

フォーマットを整えてdumpしよう

ここが初学者の間違いポイントなのですが、実際にファイルに書き出したいときはdump関数を使います。

dump関数と、dumps関数を同じものだと思っている方は多いですが、dump関数は、ファイルとして保存するための関数で、dumps関数はエンコード(辞書型を文字列に変換)するための関数であるということです。

しっかりと違いを理解するようにしましょう!

さて、実際の使い方をみていきましょう。

import json

dict = {"name": "tarou", "age": 23, "gender": "man"}

json_file = open('test.json', 'w')

json.dump(dict, json_file)

このように、open関数でファイルを指定して、dump関数で第1引数に中身、第2引数にファイルを指定することで書き出すことができます。

test.json

{"gender": "man", "age": 23, "name": "tarou"}

きちんと、書き込めていますがJSONファイルなのに、改行などがないですね。情報量が多くなれば改行なども適正に入れて見やすくしてほしいですよね。

そんなときは第3引数にインデント幅を指定してやると、フォーマットしてくれます。

import json

dict = {"name": "tarou", "age": 23, "gender": "man"}

json_file = open('test.json', 'w')

json.dump(dict, json_file, indent=2)

test.json

{
  "gender": "man", 
  "age": 23, 
  "name": "tarou"
}

可読性が向上しましたね。

datetime型をdumpsしてみよう

さて、次はdatetime型を含むディクショナリ型をエンコードしてみましょう。

import json
from datetime import datetime


dict = {"now": datetime.now()}
print(dict)

json.dumps(dict)

例えばこのように、ディクショナリの中にdatetime型のオブジェクトが入っていた場合実行すると、

{'now': datetime.datetime(2018, 11, 10, 20, 5, 23, 37646)}
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    json.dumps(dict)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'datetime' is not JSON serializable

このようにエラーになってしまいます。

JSONにはdatetime型が定義されていないためこのようなことが起こります。こういう場合は第2引数にdefaultを指定してやり、そこでエンコーダを拡張する関数を呼び出す必要があります

まずは、コードと結果からみていきましょう。

import json
from datetime import datetime

def expireEncoda(object):
    if isinstance(object, datetime):
        return object.isoformat()

dict = {"now": datetime.now()}

enc = json.dumps(dict, default=expireEncoda)

print(enc)
print(type(enc))
{"now": "2018-11-10T20:21:24.524550"}
<class 'str'>

きちんとdumpsできていますね。

default引数に関数を指定してあげると、対応してないほうのデータを引数として持ち関数を呼び出すことができます。

つまり、ここでは、objectという変数には、datetimeの内容が入っています

isinstanceとはdatetimeモジュールをインポートしたことで使えるようになった関数で第一引数の型が第二引数であるかどうかをチェックしてくれています。

そして、自作したexpireEncodaはTrueの場合そのオブジェクトを文字列で返してくれるという関数になっていすね。

少し難しいですが、実際にコードを書いてみると理解できますのでよくわからない方は手を動かしてみましょう!

日本語の文字化けに対応しよう

import json

dict = {"name": "太郎", "age": 23, "gender": "男"}
enc = json.dumps(dict, indent = 2)
print(enc)
{
  "name": "u592au90ce",
  "age": 23,
  "gender": "u7537"
}

このようにdumpsでエンコードを行っているとき、日本語が変な文字列に変換されてしまうという方もいるのではないでしょうか?

これはJSONの仕様でUnicodeが出力されているためです。そんなときは、

import json

dict = {"name": "太郎", "age": 23, "gender": "男"}
enc = json.dumps(dict, indent=2, ensure_ascii=False)
print(enc)

このように、第2引数に”ensure_ascii”を”False”を指定してやると、

{
  "name": "太郎",
  "age": 23,
  "gender": "男"
}

思うようにdumpsしてくれました。

まとめ

いかがでしたでしょうか。今日は、JSONのエンコードであるdumpsについて詳しくみていきました。

エンコード、デコードの違い、dumps、dumpの違い、様々な状況でのdumpsなどこの記事の内容を完全に理解できれば、JSONのdumpsはバッチリです!

冒頭で紹介したloadsなども合わせてマスターしてJSONマスターになりましょう!

それでは!!

この記事を書いた人

日向徹かこよすぎか

kaikaikai8217@gmail.com

目次