環境
- Python 3.12.1
- pandas 2.2.2
はじめに
pandas.DataFrame.to_json()は、スラッシュ/
を\\
でエスケープして出力します。
In [6]: import pandas as pd
In [12]: data = [{"id":"alice", "url":"https://example.com"}]
In [16]: df = pd.DataFrame(data)
In [17]: df.to_json()
Out[17]: '{"id":{"0":"alice"},"url":{"0":"https:\\/\\/example.com"}}'
しかし、json.dumps
ではスラッシュをエスケープせずに出力します。
In [15]: json.dumps(data)
Out[15]: '[{"id": "alice", "url": "https://example.com"}]'
この違いは何なのでしょうか?
JSONの仕様
JSONの仕様では、スラッシュをエスケープすることができます。ただし、スラッシュのエスケープは必須でありません。
ここで一つポイントなのは、いくつかの実装でsolidus(スラッシュ)がエスケープの対象でなかったり、オプションだったりすること。JavaScriptの規格であるECMA-262ではエスケープ表記の対象にsolidus(スラッシュ)は含まれていなかったけど、最初の独立したJSON規格であるRFC 4627以降では対象に含まれている、という違いが有る。
なお、json
モジュールはスラッシュをエスケープしてエンコードしませんが、エスケープされたスラッシュをデコードできます。
In [19]: tmp = df.to_json()
In [20]: tmp
Out[20]: '{"id":{"0":"alice"},"url":{"0":"https:\\/\\/example.com"}}'
In [21]: data2 = json.loads(tmp)
In [22]: data2
Out[22]: {'id': {'0': 'alice'}, 'url': {'0': 'https://example.com'}}
pandasの話
スタックオーバーフローの回答によると、pandas.DataFrame.to_json
ではusjonというライブラリが使われているそうです。
pandas.DataFrame.to_json()
の中身を調べていくと、pandas.is.json._json.Writer.write
関数で、pandas._libs.json.ujson_dumps
という関数を呼び出していることが分かりました。
なお、ujson
はデフォルトではスラッシュをエスケープして出力します。
スラッシュをエスケープしないようにする方法
pandas.DataFrame.to_json()
を使わずにjson.dumps
を使えば、スラッシュをエスケープせずにJSON出力できます。
In [34]: tmp2 = df.to_dict("records")
In [35]: tmp2
Out[35]: [{'id': 'alice', 'url': 'https://example.com'}]
In [36]: json.dumps(tmp2)
Out[36]: '[{"id": "alice", "url": "https://example.com"}]'
ただし、以下の点に注意してください。
NaN
はnullに変換されずにそのままNaN
が出力される
pandas.DataFrame.to_json()
はNaN
をnull
に変換します。
Note NaN’s and None will be converted to null and datetime objects will be converted to UNIX timestamps.
pandas.DataFrame.to_json
を使わずに、pandas.DataFrame.to_dict
で変換してjson.dumps
でJSONを出力すると、NaN
はそのままNaN
と出力されます。
In [38]: df = pd.DataFrame({"value": [1.1, None]})
In [39]: df
Out[39]:
value
0 1.1
1 NaN
In [41]: tmp2 = df.to_dict("records")
In [42]: tmp2
Out[42]: [{'value': 1.1}, {'value': nan}]
In [43]: json.dumps(tmp2)
Out[43]: '[{"value": 1.1}, {"value": NaN}]'
NaN
はJSONの仕様ではありませんが、Pythonのjson
モジュールはNaN
に対応しています。
In [52]: json.loads("NaN")
Out[52]: nan
pandas.DataFrame.to_dict
はpandas.NA
をNone
に変換する
pandas.DataFrame.to_dict
はpandas.NA
をNone
に変換します。
したがって、pandas.DataFrame.to_json
を使わない場合は、DataFrameの列の型をfloat64
ではなくnullableなFloat64
にした方がよいでしょう。
In [55]: df = pd.DataFrame({"value": [1.1, None]}, dtype="Float64")
In [56]: df
Out[56]:
value
0 1.1
1 <NA>
In [57]: tmp3 = df.to_dict("records")
In [58]: tmp3
Out[58]: [{'value': 1.1}, {'value': None}]
まとめ
-
pandas.DataFrame.to_json
はスラッシュをエスケープして出力する - JSONの仕様では、スラッシュをエスケープして出力することは必須でない
-
pandas.DataFrame.to_json
はujson
ライブラリを使ってJSONを出力している-
ujson
はデフォルトでは、スラッシュをエスケープして出力する
-
-
pandas.DataFrame.to_json
はNaN
をnull
に変換して出力する
参考にしたサイト