95086
# 環境

  * Python 3.11.2

# はじめに

日本語を含むオブジェクトをJSON文字列に変換する場合、`ensure_ascii=False`を指定する必要があります。

```

    In [489]: d = {"x":"あ"}
    
    In [490]: json.dumps(d)
    Out[490]: '{"x": "\\u3042"}'
    
    In [491]: json.dumps(d, ensure_ascii=False)
    Out[491]: '{"x": "あ"}'
    
```

> ensure_ascii が (デフォルト値の) true の場合、出力では入力された全ての非 ASCII
> 文字はエスケープされていることが保証されています。ensure_ascii が false の場合、これらの文字はそのまま出力されます。

<https://docs.python.org/ja/3.11/library/json.html#json.dump>

# 質問

なぜ`ensure_ascii`引数はデフォルトで`True`なのでしょうか？  
JSON文字を出力する場合は、非ASCII文字もその出力したいケースの方が多いと思いました。

また、たとえばJavaScriptの[JSON.stringfy](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)では、Pythonの`ensure_ascii`に相当する引数はありません。

```

    > console.log(JSON.stringify({ x: "あ"}));
    {"x":"あ"}
    
```

なぜPythonでは、`ensure_ascii`のような機能が存在するのかが疑問に思いました。

Python2から続く歴史的経緯なのでしょうか？

# 調べたこと

以下の質問が関連しそうですが、Python2に関する質問のためか、あまり理解できませんでした。

<https://stackoverflow.com/questions/40412714/using-json-dumps-with-ensure-
ascii-true>
CC BY-SA 4.0
2023-06-03T01:41:47.823
95082
2023-06-04T22:57:45.677
19524
question
python json
`json.dump`関数の`ensure_ascii`引数は、なぜデフォルトで`True`なのでしょうか？
JSONは文字列のエンコーディングに関する規定が存在しません。[json.org](https://www.json.org/json-
ja.html)などを見れば書かれていないことが分かると思います。そのため、日本語であればShift-JISかもしれませんし、UTF-8かもしれません。

デコーダーがどのように解釈するか保証がないため、安全を考慮すればエンコーダーは非ASCII文字を全てエスケープする、という判断は妥当かと。  
なお、エスケープする際はUTF-16を使用して`\u1234`と表現することが定められていますので、この点は曖昧性がありません。

* * *

> 最初のJSONの仕様であるRFC4627には、
>

>> JSON text SHALL be encoded in Unicode. The default encoding is UTF-8.

>
> とありました。 したがって、エンコーディングがShift_JISでないことは確かだと思いました。

RFC化される前からJSONは使われていました。そもそもJavaScript上で`eval`でデコードできることが目的でしたし。そして、その当時はJSONを含むJavaScriptコードのエンコーディングは定められていませんでした。

後からRFC等が制定されたとしても、過去に作られたプログラムがなくなるわけではなく、互換性を考慮する必要があるわけです。
CC BY-SA 4.0
2023-06-03T07:18:47.317
95084
2023-06-04T22:57:45.677
2023-06-04T22:57:45.677
4236
4236
95082
answer
[JSON は UTF-8
で符号化する必要がある](https://datatracker.ietf.org/doc/html/rfc8259#section-8.1)のですが、このルールは
JSON
が最初にできた後に追加された規定です。このルール以前に作られたシステムだと非ASCII文字をUTF-8で符号化しても正しく読めない可能性があるため、ASCIIで出力したほうが無難となります。
CC BY-SA 4.0
2023-06-03T08:36:11.617
95085
2023-06-03T08:36:11.617
3475
95082
answer
Pythonの以下の処理は, 何を表示しようとしてるかは分かっても  
結果が見える場合と見えない場合があります  
日本語フォントや絵文字フォントがロードされてれば, 日本語や絵文字が表示されるけど, なければトーフ(豆腐: たぶん何らかの四角のようなもの) になるはず

```

    >>> '\N{GRINNING FACE}'
    ''
    
```

* * *

JSON仕様はこちら

  * (json.org) [JSON の紹介](https://www.json.org/json-ja.html) より, PDF [ECMA-404 The JSON Data Interchange Standard](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)
  * IETF [RFC 8259](https://www.rfc-editor.org/info/rfc8259)
    * 適当に見つけた和訳: [JSON(JavaScript Object Notation)データ交換フォーマット](https://www.asahi-net.or.jp/%7Eax2s-kmtn/internet/rfc8259j.html)

それによると

>   7. 文字列  
>
> 文字列の表現は、プログラミング言語のCファミリーで用いられている規定に似ています。文字列は、引用符で始まり、引用符で終わります。エスケープしなければならない(MUST)文字(引用符、バックスラッシュ、および制御文字(U+0000からU+0000))を除くすべてのUnicode文字を引用符内に置くことができます。  
>
> 任意の文字をエスケープできます。文字が基本多言語面(U+0000からU+FFFF)にある場合、6文字のシーケンス(バックスラッシュの後に小文字のu、その後にその文字のコードポイントをエンコードした4桁の16進が続く)として表すことができます。16進文字のAからFは、大文字でも小文字でもかまいません。したがって、例えば、1つのバックスラッシュ文字のみを含む文字列は、「\u005C」と表すことができます。  
>
> または、いくつかのよく知られている2文字のシーケンス・エスケープ表現もあります。したがって、例えば、1つのバックスラッシュ文字のみを含む文字列は、「\」と、よりコンパクトに表すことができます。  
>
> 基本多言語面にない拡張文字をエスケープするためには、UTF-16サロゲート・ペアをエンコードした12文字のシーケンスとして文字を表します。したがって、例えば、ト音記号(U+1D11E)のみを含む文字列は、「\uD834\uDD1E」と表すことができます。
>

>   8. 文字列と文字に関する課題  
>  8.1. 文字エンコーディング  
>
> 閉じたエコシステムの一部ではないシステムの間で交換されるJSONテキストは、UTF-8[RFC3629]を用いてエンコードしなければなりません(MUST)。
>

[基本多言語面](https://ja.wikipedia.org/wiki/%E5%9F%BA%E6%9C%AC%E5%A4%9A%E8%A8%80%E8%AA%9E%E9%9D%A2)
にひらがななども含まれます  
「表すことができます」としか記されてなさそうなので,  
(そのデフォルトが Python2 との関連であったかもだが) あとは 状況により選択するのでは？

  * 視認性重視するかどうか (先頭に記したこと)
  * i18n 考慮する必要があるか (先頭に記したこと)
  * サイズ考慮する必要があるか (Unicode エスケープシーケンス使うなら少しサイズが大きくなる)
  * 脆弱性の考慮 (たとえばスパムメール対策で全角表現するように, エスケープする方がよい場合も)
  * その他
CC BY-SA 4.0
2023-06-03T09:07:13.950
95086
2023-06-03T09:07:13.950
43025
95082
answer
公式ドキュメント [:mod:`json` --- JSON encoder and decoder(cpython/json.rst at main ·
python/cpython ·
GitHub)](https://github.com/python/cpython/blob/main/Doc/library/json.rst)
から引用。

※ 現時点での Python では、RFC 8259 ではなくて RFC 7159 をリファレンスとしています。

> JSON (JavaScript Object Notation), specified by **RFC 7159** (which
> obsoletes RFC 4627) and by ECMA-404, is a lightweight data interchange
> format inspired by JavaScript object literal syntax (although it is not a
> strict subset of JavaScript [1] ).

> [Character
> Encodings](https://github.com/python/cpython/blob/main/Doc/library/json.rst#character-
> encodings)
>
> Be cautious when parsing JSON data from untrusted sources. A malicious JSON
> string may cause the decoder to consume considerable CPU and memory
> resources. Limiting the size of data to be parsed is recommended.
>
> **The RFC requires that JSON be represented using either UTF-8, UTF-16, or
> UTF-32, with UTF-8 being the recommended default for maximum
> interoperability**.
>
> **As permitted, though not required, by the RFC, this module's serializer
> sets ensure_ascii=True by default** , thus escaping the output so that the
> resulting strings only contain ASCII characters.
>
> Other than the ensure_ascii parameter, this module is defined strictly in
> terms of conversion between Python objects and :class:`Unicode strings `,
> and thus does not otherwise directly address the issue of character
> encodings.
>
> The RFC prohibits adding a byte order mark (BOM) to the start of a JSON
> text, and this module's serializer does not add a BOM to its output. The RFC
> permits, but does not require, JSON deserializers to ignore an initial BOM
> in their input. This module's deserializer raises a :exc:`ValueError` when
> an initial BOM is present.
>
> The RFC does not explicitly forbid JSON strings which contain byte sequences
> that don't correspond to valid Unicode characters (e.g. unpaired UTF-16
> surrogates), but it does note that they may cause interoperability problems.
> By default, this module accepts and outputs (when present in the original
> :class:`str`) code points for such sequences.
CC BY-SA 4.0
2023-06-03T10:06:47.533
95087
2023-06-03T10:06:47.533
47127
95082
answer