要点
- Pythonの既定設定はNaN・Infinityをそのまま出力し、JavaScriptはnullに変換した(出典: 自社の実測・2026-10-25)
- Goのjson.Marshalは、NaNを含む値の変換でエラーを返した(出典: 自社の実測・2026-10-25)
- Pythonもallow_nan=Falseを指定すると、NaNの変換で例外が発生した(出典: 自社の実測・2026-10-25)
- Pythonが既定設定で出力したNaN入りの文字列は、JavaScriptのJSON.parseで読み込めなかった(出典: 自社の実測・2026-10-25)
NaNをJSONに変換すると何が困るのか
NaNとは、非数を表す値のことだ。JSON仕様には、このNaNや、無限大を表すInfinityを書き表す方法がない。それでも、これらを含むデータをJSONに変換しようとした際の対応は、言語ごとに異なる。
Pythonのjson.dumpsでは、既定設定のままでも変換処理がエラーにならない。しかし、その出力にはNaNやInfinityが残る。変換が完了したことだけを見て、受信側でも読み込めると判断することはできない。
一方、JavaScriptのJSON.stringifyは対象の値をnullに変換し、Goのjson.Marshalはエラーを返す。「そのまま出力する」「別の値に置き換える」「変換を止める」という違いだ。APIの応答や他システムへの連携データを作る場面では、まずこの違いを区別したい。
比較の問いは、どの言語がJSONを扱えるかではなく、JSONに表せない値が渡されたとき、どのように処理するかである。今回の記録では、送信側の変換だけでなく、Pythonの出力をJavaScriptで読み込むところまで確かめている。
公式資料は出力・置換・エラーをどう説明しているか
Python公式ドキュメントのjson.dumpについて、取得記録には次の記述がある。
If allow_nan is false… a ValueError will be raised if… float('nan')… Default is True
出典: https://docs.python.org/3/library/json.html#json.dump (取得日 2026-10-25)
これは取得記録に収められた、省略を含む引用である。allow_nanの既定値はTrueであり、FalseにするとNaNなどに対してValueErrorを送出するという説明だ。実測ではjson.dumpsにこの設定を指定し、例外が発生することを確認している。
JavaScriptのJSON.stringify()について、MDNの取得記録には次の記述がある。
NaN and Infinity… are all considered null
出典: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify (取得日 2026-10-25)
こちらはnullとして扱うという説明であり、Pythonの既定設定とは処理が違う。出力が得られても、元の値がNaNだったのかInfinityだったのかは、そのnullからは区別できない。
Goのencoding/jsonについては、取得記録に次の記述がある。
JSON cannot represent… NaN and infinity… Marshal returns an UnsupportedValueError
出典: https://pkg.go.dev/encoding/json (取得日 2026-10-25)
Goは、表せない値を出力に残すのでも、nullに置き換えるのでもなく、エラーを返す。公式資料の説明を並べるだけでも、同じ入力に対する処理方針が異なることが分かる。
実測で比べると、Pythonの設定変更でも差が出る
実測日は2026-10-25。記録された環境はPython 3.9.0、Node.jsのv22.23.4、Goのgo version go1.26.2 darwin/arm64である。以下は、この環境で確認した結果を整理したものだ。
| 対象 | 実測で渡した値 | 出力・結果 | エラーの有無 |
|---|---|---|---|
| Python・既定設定 | NaN、Infinity | {"a": NaN, "b": Infinity} |
なし |
Python・allow_nan=False |
NaN | 例外が発生 | あり |
| JavaScript | NaN、Infinity、-Infinity | {"a":null,"b":null,"c":null} |
なし |
| Go | NaN、Infinity | 返されたデータを文字列にすると空文字列 | あり |
Pythonの既定設定について、記録にあるコードと出力は次のとおりだ。
import json
print(json.dumps({"a": float("nan"), "b": float("inf")}))
# {"a": NaN, "b": Infinity}
波括弧やキーを含む見た目だけでは、JSON仕様にない値が残っていることを見落としかねない。この記録では、NaNとInfinityが出力にそのまま現れている。
同じPythonでも、設定を変えた実測では結果が変わった。
import json
try:
print(json.dumps({"a": float("nan")}, allow_nan=False))
except Exception as e:
print("strict error:", e)
# strict error: Out of range float values are not JSON compliant
allow_nan=Falseは、NaNをnullに置き換える設定ではない。今回の実測で確認できたのは、例外を発生させて変換を止める動作だ。「json dumps nan to null」という目的で調べている場合も、この設定を置換の方法として扱わないようにしたい。
また、入力条件は全行で完全に同一ではない。負の無限大まで含めた実測はJavaScriptの行であり、Pythonの厳密設定の実測入力はNaNのみである。表は各記録の範囲を示しており、未実測の組み合わせを追加したものではない。
出典: 自社の実測(2026-10-25)
他システム向けなら、出力時に止める設定を選ぶ
Pythonで他システム向けのJSONを作る際は、allow_nan=Falseを指定し、変換前の値も検査してエラーとして扱う方法が選択肢になる。既定設定では通ってしまう値を、送信データを作る段階で検出できるためだ。
受信側での問題は、今回の記録でも確認されている。Pythonが出力した{"a": NaN, "b": Infinity}をJavaScriptのJSON.parse()に渡すと、解析エラーになった。送信側で変換処理が終わっていても、受信側で処理が続けられるとは限らない。
ここでは、JavaScriptの出力処理と入力処理も分けて考える必要がある。JSON.stringifyがNaNをnullに変えることと、JSON.parseがNaNを含む文字列を受け入れることは別の話だ。前者の実測では置換され、後者の実測では読み込めなかった。
nullへの置換を選ぶ場合には、NaNやInfinityという元の値の違いが出力から失われる点も判断材料になる。一方、エラーで止める方法では、その入力を扱うための対応が必要になる。出力が得られるかだけでなく、値の違いを残す必要があるか、変換時に異常として扱うかを整理して選びたい。
なお、配列要素については個別の実測がない。記録には同じ変換ロジックを通ることをコードで確認したとあるが、今回の表と同じ条件で測定した結果としては扱えない。JavaやPHPのライブラリも未測定である。
出典: 自社の実測(2026-10-25)
自分の環境で確かめる手順
まずPythonの版を確認し、同じNaN・Infinityを既定設定とallow_nan=Falseで渡す。次のコマンドは読者が自分の環境で実行するための手順であり、ここには新しい実行結果を掲載していない。
python3 --version
python3 -c 'import json; print(json.dumps({"a": float("nan"), "b": float("inf")}))'
python3 -c 'import json; print(json.dumps({"a": float("nan"), "b": float("inf")}, allow_nan=False))'
確認するのは、変換後の文字列に何が残るかと、設定を変えると例外が発生するかである。実際に連携する場面では、作った文字列を受信側のパーサーでも確認したい。今回の実測が示した落とし穴は、Python側で出力できたことだけでは、JavaScript側で読み込めることを保証できない点にある。

コメント