json.dumps nan:PythonとJS・Goの違いは?

要点

  • 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側で読み込めることを保証できない点にある。

コメント

タイトルとURLをコピーしました