

## 背景/要求

`tuple[str]` をキーとする `dict` を子孫要素として含むような `dict` を `json.dump` で出力したいです。JSON
のキーとして `tuple[str]` は許されていないため、当然そのまま実行するとエラーが発生しました。

[code]

    TypeError: keys must be str, int, float, bool or None, not tuple
    
[/code]

そこでキーを `str` へ変換する関数を作成しました。たとえば

[code]

    def key_converter(key: tuple[str]):
        return f"THIS_IS_A_TUPLE_OF_STRS:{key}"
    
[/code]

これを使用して `json.dump` を行いたいです。

## 試したことと結果、および考えられる理由

### `dump(default=key_converter)`

同じエラー。

`json.encoder._make_iterencode._iterencode_dict` の実装に

[code]

    if isinstance(key, str):
        pass
    # JavaScript is weakly typed for these, so it makes sense to
    # also allow them.  Many encoders seem to do something like this.
    elif ...
    
[/code]

という部分があるものの、具体的な変換にとどまっており、ユーザから与えた `default` は key の変換には使われず value
の変換にのみ使われていることを確認しました。

### `dump(default=dict_key_converter)`

同じエラー。

ここで `dict_key_converter` は「引数が `dict` であれば、各 key に `key_converter` を適用する」関数です。

こちらも `json.encoder._make_iterencode` 内を見ると、`isinstance(o, dict)` が成り立つときは
`_iterencode_dict` を (成功する前提で) 呼び出しており、これでは `_default` が呼ばれないことも確認しました。

## 考えられるが試していないこと、および試していない理由

### a. 事前に変換する

データ構造にわたる再帰を (`json` モジュールに任せているのに、それと重複して) 書くのは、あまり賢明ではないと考えています。

### b. `dict` のように振る舞うが `dict` のサブクラスではない実装に変更する

ラップして (必要な)
機能の委譲を行えば可能だと思いますが、無意味なコードが増え、かつ他の部分で利用する際の拡張性が犠牲になると考えています。なお、現在利用しているのは
`defaultdict` なので、ラップする手間も大きいです。

### c. `dict` のネストに変更する

JSON 化するなら `{(a, b): c}` を `{a: {b: c}}` とするべきであったかもしれません。今回は a, b, c
の制約を活かせるのでエンコードするだけなら問題ありません。ただし、デコード時にはこれらを区別するために、もう一工夫が必要そうです。

### d. `json` 自体の機能追加

最初に試した方法に関する調査を考えると、コード修正そのものの方針は立ちます。

  * 私のスキルでは、Issue や PR を通して解決してもらうことは、こちらで質問するより時間がかかりそうです。
  * 独自パッチは当面の対応としては考えられますがメンテナンスは考えたくありません。

## 求める解決策

  * a のシンプルな実装があれば JSON化の直前だけ呼び出したいです。
  * 簡単な `wrap/unwrap` の方法があれば b と組み合わせて今回の用途には十分です。
  * デコード時まで考えた c の方針、または `{(a, b): c}` と `{a: {b: c}}` とがエンコード時にも混在していて区別しなければならないような一般の状況にも対処できる方針があれば、より参考になります。
  * これらより良い解決策もあるのではないかと思いますが私には見つかっていません。


CC BY-SA 4.0
2022-01-08T23:55:35.847
85570
2023-07-25T08:08:51.110
43417
question
python json
python.json.dump で dict の key に default を適用させたい (TypeError: keys must be str, int, float, bool or None, not tuple)


理解が合っているかどうか確信がありませんが、引数を先に変換すれば良いのではないでしょうか？

[code]

    json.dumps({str(k): v for k, v in obj.items()})
    
[/code]


CC BY-SA 4.0
2022-01-25T06:51:26.070
85922
2022-01-25T06:51:26.070
51087
85570
answer


こういった方法はどうでしょうか？  
おそらく、一部がタプルがキーとなる辞書型になっているのだろうと推測します。

tupleを文字列に変換して辞書型にします。

[code]

    keys = ("A1","A2")
    data_dict = {str(keys):"data"}
    
[/code]

辞書型からの取り出しの時は、キーがstr型の場合に、正規表現とsplit(",")でキーをタプルに戻して判定します。  
もし、キーの条件判定で取り出すだけならば、上記辞書からのキー復元は不必要で、判定するtuple型キーをstr型にして、判断すればよいので、複雑ではないと思います。


CC BY-SA 4.0
2023-05-02T01:19:36.490
94715
2023-05-02T01:19:36.490
32891
85570
answer