JSON から Unicode の代わりに文字列オブジェクトを取得するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/956867

質問

使っています パイソン2 JSONを解析する ASCIIエンコード テキストファイル。

これらのファイルをいずれかの方法でロードすると、 json または simplejson, 、すべての文字列値は文字列オブジェクトではなく Unicode オブジェクトにキャストされます。問題は、文字列オブジェクトのみを受け入れるいくつかのライブラリでデータを使用する必要があることです。私 ライブラリを変更できません 更新もしないでください。

Unicode オブジェクトの代わりに文字列オブジェクトを取得することはできますか?

>>> import json
>>> original_list = ['a', 'b']
>>> json_list = json.dumps(original_list)
>>> json_list
'["a", "b"]'
>>> new_list = json.loads(json_list)
>>> new_list
[u'a', u'b']  # I want these to be of type `str`, not `unicode`

アップデート

この質問がされました ずっと前に, に行き詰まったとき、 パイソン2. 。今日の簡単でクリーンな解決策の 1 つは、Python の最新バージョンを使用することです。 パイソン3 そして前へ。

役に立ちましたか?

解決

による解決策 object_hook

import json

def json_load_byteified(file_handle):
    return _byteify(
        json.load(file_handle, object_hook=_byteify),
        ignore_dicts=True
    )

def json_loads_byteified(json_text):
    return _byteify(
        json.loads(json_text, object_hook=_byteify),
        ignore_dicts=True
    )

def _byteify(data, ignore_dicts = False):
    # if this is a unicode string, return its string representation
    if isinstance(data, unicode):
        return data.encode('utf-8')
    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item, ignore_dicts=True) for item in data ]
    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict) and not ignore_dicts:
        return {
            _byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True)
            for key, value in data.iteritems()
        }
    # if it's anything else, return it in its original form
    return data

使用例:

>>> json_loads_byteified('{"こんにちは":"世界"}')
{'Hello': 'World'}
>>> json_loads_byteified('"私はトップレベルの文字列です"')
'I am a top-level string'
>>> json_loads_byteified('7')
7
>>> json_loads_byteified('["私はリストの中にいます"]')
['I am inside a list']
>>> json_loads_byteified('[[[[[[[["私はリストの大きな入れ子の中にいます"]]]]]]]')
[[[[[[[['I am inside a big nest of lists']]]]]]]]
>>> json_loads_byteified('{"foo":「バー」、「物」:[7、{"qux":「バズ」、「ムー」:{"牛":["牛乳"]}}]}')
{'things': [7, {'qux': 'baz', 'moo': {'cow': ['milk']}}], 'foo': 'bar'}
>>> json_load_byteified(open('somefile.json'))
{'more json': 'from a file'}

これはどのように機能し、なぜ使用するのでしょうか?

マーク・アメリーの関数 はこれらよりも短くて明確ですが、これらに何の意味があるのでしょうか?なぜそれらを使用したいのですか?

純粋に パフォーマンス. 。マークの答えは、最初に Unicode 文字列を使用して JSON テキストを完全にデコードし、次にデコードされた値全体を再帰してすべての文字列をバイト文字列に変換します。これには、いくつかの望ましくない影響があります。

  • デコードされた構造全体のコピーがメモリ内に作成されます
  • JSON オブジェクトが 本当に 深くネストされている場合 (500 レベル以上)、Python の最大再帰深度に達します。

この回答では、 object_hook のパラメータ json.load そして json.loads. 。から ドキュメント:

object_hook 任意のオブジェクト リテラル デコードされた結果を使用して呼び出されるオプションの関数です ( dict)。object_hook の戻り値は、 dict. 。この機能を使用してカスタム デコーダを実装できます。

辞書は他の辞書に多くのレベルで深くネストされているため、他の辞書に渡されます。 object_hook デコードされると, を使用すると、その時点で内部の文字列またはリストをバイト化できるので、後で深い再帰を行う必要がなくなります。

マークの答えは、 object_hook ネストされた辞書を再帰するため、現状のままです。この回答では、次のようにしてその再帰を防ぎます。 ignore_dicts パラメータを _byteify, 、常に渡されます を除外する いつ object_hook 新しいものを渡します dict バイトすること。の ignore_dicts フラグが伝える _byteify 無視する dictすでにバイト化されているため。

最後に、私たちの実装は、 json_load_byteified そして json_loads_byteified 電話 _byteify (と ignore_dicts=True) から返された結果について json.load または json.loads デコードされる JSON テキストに dict トップレベルで。

他のヒント

ここにはいくつかの良い答えがありますが、私は最終的に使用しました PyYAML キーと値が次のように与えられるため、JSON ファイルを解析します。 str 代わりに文字列を入力します unicode タイプ。JSON は YAML のサブセットであるため、うまく動作します。

>>> import json
>>> import yaml
>>> list_org = ['a', 'b']
>>> list_dump = json.dumps(list_org)
>>> list_dump
'["a", "b"]'
>>> json.loads(list_dump)
[u'a', u'b']
>>> yaml.safe_load(list_dump)
['a', 'b']

ノート

ただし、注意すべき点がいくつかあります。

  • 分かりました 文字列オブジェクト 私のエントリーはすべて ASCIIエンコード. 。Unicode でエンコードされたエントリを使用すると、次のように返されます。 ユニコードオブジェクト — 変換はありません!

  • (おそらく常に) PyYAML を使用する必要があります。 safe_load 関数;JSON ファイルのロードに使用する場合は、 load とにかく機能します。

  • 仕様の 1.2 バージョンをさらにサポートする YAML パーサーが必要な場合 (および 非常に小さい数値を正しく解析します) 試す ルアメル YAML: pip install ruamel.yaml そして import ruamel.yaml as yaml テストで必要なのはそれだけでした。

変換

前述したように、変換はありません。ASCII 値のみを扱うかどうか確信が持てない場合 (そして、ほとんどの場合確信が持てない場合)、 変換関数:

からのものを使用しました マーク・アメリー 今、何度か使っていますが、うまく機能し、とても使いやすいです。同様の関数を次のように使用することもできます。 object_hook 代わりに、大きなファイルのパフォーマンスが向上する可能性があるためです。もう少し複雑な部分を参照してください ミレック・ミスクフさんからの回答 そのために。

json モジュール関数が Unicode 文字列の代わりにバイト文字列を返すようにする組み込みオプションはありません。ただし、この短くて単純な再帰関数は、デコードされた JSON オブジェクトを Unicode 文字列の使用から UTF-8 でエンコードされたバイト文字列に変換します。

def byteify(input):
    if isinstance(input, dict):
        return {byteify(key): byteify(value)
                for key, value in input.iteritems()}
    elif isinstance(input, list):
        return [byteify(element) for element in input]
    elif isinstance(input, unicode):
        return input.encode('utf-8')
    else:
        return input

から得た出力に対してこれを呼び出すだけです。 json.load または json.loads 電話。

いくつかのメモ:

  • Python 2.6 以前をサポートするには、次のように置き換えます。 return {byteify(key): byteify(value) for key, value in input.iteritems()}return dict([(byteify(key), byteify(value)) for key, value in input.iteritems()]), Python 2.7 までは辞書内包表記がサポートされていなかったためです。
  • この回答はデコードされたオブジェクト全体を通じて再帰的に行われるため、望ましくないパフォーマンス特性がいくつかありますが、これは非常に慎重に使用することで回避できます。 object_hook または object_pairs_hook パラメーター。 ミレク・ミスクフの答え これを正しく実行できるのは、これまでのところ唯一の方法ですが、結果として、私のアプローチよりも大幅に複雑になります。

使用できます object_hook のパラメータ json.loads コンバーターに渡します。事後に変換を行う必要はありません。の json モジュールは常に object_hook 辞書のみであり、ネストされた辞書を再帰的に渡すため、自分でネストされた辞書を再帰する必要はありません。ウェルズ氏が示したように、Unicode 文字列を数値に変換するとは思いません。Unicode 文字列の場合は、JSON ファイル内で文字列として引用されているため、文字列であると考えられます (またはファイルが間違っています)。

また、次のようなことは避けたいと思います str(val)unicode 物体。使用する必要があります value.encode(encoding) 外部ライブラリが何を期待しているかに応じて、有効なエンコーディングを使用してください。

たとえば、次のようになります。

def _decode_list(data):
    rv = []
    for item in data:
        if isinstance(item, unicode):
            item = item.encode('utf-8')
        elif isinstance(item, list):
            item = _decode_list(item)
        elif isinstance(item, dict):
            item = _decode_dict(item)
        rv.append(item)
    return rv

def _decode_dict(data):
    rv = {}
    for key, value in data.iteritems():
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        elif isinstance(value, list):
            value = _decode_list(value)
        elif isinstance(value, dict):
            value = _decode_dict(value)
        rv[key] = value
    return rv

obj = json.loads(s, object_hook=_decode_dict)

これは、json には文字列オブジェクトと Unicode オブジェクトの違いがないためです。これらはすべて JavaScript の文字列です。

私は思う JSON は Unicode オブジェクトを返すのが正しいです. 。実際、私はそれ以下のものは受け入れません。 実際にいます unicode オブジェクト (すなわち、JSON (JavaScript) 文字列を格納できる いずれかの種類 Unicode 文字の)なので、作成するのは理にかなっています unicode JSON から文字列を変換するときのオブジェクト。ライブラリは必要なエンコーディングを推測する必要があるため、プレーンな文字列は適合しません。

使ったほうがいいよ unicode どこにでもある文字列オブジェクト。したがって、最善の選択肢は、Unicode オブジェクトを処理できるようにライブラリを更新することです。

ただし、本当にバイト文字列が必要な場合は、選択したエンコーディングで結果をエンコードするだけです。

>>> nl = json.loads(js)
>>> nl
[u'a', u'b']
>>> nl = [s.encode('utf-8') for s in nl]
>>> nl
['a', 'b']

簡単な回避策があります。

TL;DR - 使用する ast.literal_eval() の代わりに json.loads(). 。両方 ast そして json 標準ライブラリにあります。

「完璧な」答えではありませんが、Unicode を完全に無視する計画であれば、かなり近い答えになります。Python 2.7 の場合

import json, ast
d = { 'field' : 'value' }
print "JSON Fail: ", json.loads(json.dumps(d))
print "AST Win:", ast.literal_eval(json.dumps(d))

与える:

JSON Fail:  {u'field': u'value'}
AST Win: {'field': 'value'}

一部のオブジェクトが実際には Unicode 文字列である場合、これはさらに困難になります。完全な答えはすぐにわかりにくくなります。

マイク・ブレナンの答え は近いですが、構造全体を再横断する理由はありません。を使用する場合は、 object_hook_pairs (Python 2.7+) パラメータ:

object_pairs_hook は、順序付きペアのリストでデコードされたオブジェクト リテラルの結果を使用して呼び出されるオプションの関数です。の戻り値 object_pairs_hook の代わりに使用されます dict. 。この機能を使用すると、キーと値のペアがデコードされる順序に依存するカスタム デコーダーを実装できます (たとえば、 collections.OrderedDict 挿入順序は記憶されます)。もし object_hook も定義されており、 object_pairs_hook が優先されます。

これを使用すると、各 JSON オブジェクトが渡されるため、再帰を必要とせずにデコードを行うことができます。

def deunicodify_hook(pairs):
    new_pairs = []
    for key, value in pairs:
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        new_pairs.append((key, value))
    return dict(new_pairs)

In [52]: open('test.json').read()
Out[52]: '{"1": "hello", "abc": [1, 2, 3], "def": {"hi": "mom"}, "boo": [1, "hi", "moo", {"5": "some"}]}'                                        

In [53]: json.load(open('test.json'))
Out[53]: 
{u'1': u'hello',
 u'abc': [1, 2, 3],
 u'boo': [1, u'hi', u'moo', {u'5': u'some'}],
 u'def': {u'hi': u'mom'}}

In [54]: json.load(open('test.json'), object_pairs_hook=deunicodify_hook)
Out[54]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

を使用するとすべてのオブジェクトがフックに渡されるため、フックを再帰的に呼び出す必要がないことに注意してください。 object_pairs_hook. 。リストについては注意する必要がありますが、ご覧のとおり、リスト内のオブジェクトは適切に変換され、それを実現するために再帰する必要はありません。

編集:同僚が、Python2.6 には機能がないことを指摘しました。 object_hook_pairs. 。非常に小さな変更を加えることで、これを Python2.6 でも使用できます。上記のフックで、次のように変更します。

for key, value in pairs:

for key, value in pairs.iteritems():

次に、使用します object_hook の代わりに object_pairs_hook:

In [66]: json.load(open('test.json'), object_hook=deunicodify_hook)
Out[66]: 
{'1': 'hello',
 'abc': [1, 2, 3],
 'boo': [1, 'hi', 'moo', {'5': 'some'}],
 'def': {'hi': 'mom'}}

使用する object_pairs_hook その結果、JSON オブジェクト内のオブジェクトごとにインスタンス化される辞書が 1 つ少なくなります。これは、巨大なドキュメントを解析する場合には価値があるかもしれません。

残念ながら、simplejson ライブラリ内でこれを自動的に実現する方法はありません。

simplejson のスキャナーとデコーダーは、Unicode テキストを生成するように設計されています。これを行うために、ライブラリは次の関数を使用します。 c_scanstring (速度向上のため、利用可能な場合)、または py_scanstring C バージョンが利用できない場合。の scanstring この関数は、simplejson がテキストを含む可能性のある構造をデコードするために備えているほぼすべてのルーチンによって数回呼び出されます。モンキーパッチを適用する必要があります scanstring simplejson.decoder の値、またはサブクラス JSONDecoder そして、テキストを含む可能性のあるものについては、ほぼ完全な独自の実装を提供します。

ただし、simplejson が unicode を出力する理由は、 json仕様 具体的には、「文字列は 0 個以上の Unicode 文字のコレクションである」と述べています...Unicode のサポートは、形式自体の一部として想定されています。シンプルジェイソンの scanstring この実装では、Unicode エスケープのスキャンと解釈 (不正なマルチバイト文字セット表現のエラー チェックまで) まで行われるため、値を確実に返すことができる唯一の方法は Unicode として返すことです。

古いライブラリが必要な場合は、 str, 、解析後にネストされたデータ構造を苦労して検索することをお勧めします(これは避けたいと明示的に述べたことであることは認めます...申し訳ありません)、あるいは、より詳細なレベルで入力パラメータをマッサージできる、ある種のファサードでライブラリをラップすることもできます。データ構造が実際に深くネストされている場合、2 番目のアプローチは最初のアプローチよりも管理しやすい可能性があります。

マーク(アメリー)が正しく指摘しているように:使用する PyYamljson ダンプのデシリアライザーは、ASCII のみを使用している場合にのみ機能します。少なくとも箱から出した状態では。

PyYaml アプローチに関する 2 つの簡単なコメント:

  1. 一度もない フィールドからのデータに対して yaml.load を使用します。yaml の機能 (!) は、構造内に隠された任意のコードを実行します。

  2. あなた できる これを介して非ASCIIでも機能するようにします。

    def to_utf8(loader, node):
        return loader.construct_scalar(node).encode('utf-8')
    yaml.add_constructor(u'tag:yaml.org,2002:str', to_utf8)
    

しかし、パフォーマンスに関しては、Mark Amery の答えとは比べものになりません。

深くネストされたサンプル辞書を 2 つのメソッドにスローすると、次のようになります (dt[j] = json.loads(json.dumps(m)) の時間デルタ)。

     dt[yaml.safe_load(json.dumps(m))] =~ 100 * dt[j]
     dt[byteify recursion(Mark Amery)] =~   5 * dt[j]

したがって、ツリーを完全に実行することを含む逆シリアル化 そして エンコーディングは、json の C ベースの実装の規模の範囲内に収まります。これは非常に高速であり、深くネストされた構造での yaml ロードよりも堅牢であることがわかりました。yaml.load を見ると、セキュリティ エラーが発生しにくくなります。

=> C のみベースのコンバータへのポインタはありがたいですが、 バイト化関数 がデフォルトの答えになるはずです。

これは、JSON 構造がユーザー入力を含むフィールドからのものである場合に特に当てはまります。それならおそらく歩く必要があるから ともかく 構造上で - 必要な内部データ構造 (「Unicode サンドイッチ」またはバイト文字列のみ) に依存しません。

なぜ?

ユニコード 正規化. 。知らない人のために:鎮痛剤を飲んで本を読む これ.

したがって、byteify 再帰を使用すると、一石二鳥になります。

  1. ネストされた JSON ダンプからバイト文字列を取得します
  2. ユーザー入力値を正規化して、ストレージ内の情報を見つけられるようにします。

私のテストでは、input.encode('utf-8') を unicodedata.normalize('NFC', input).encode('utf-8') に置き換えると、NFC を使用しない場合よりもさらに高速であることがわかりました。それはサンプルデータに大きく依存していると思います。

問題はそれです simplejson そして json 少なくとも Unicode の処理方法においては、2 つの異なるモジュールです。あなたが持っている json py 2.6以降では、これによりUnicode値が得られますが、 simplejson 文字列オブジェクトを返します。あなたの環境で simplejson を easy_install してみて、それが機能するかどうかを確認してください。私にとってはそうでした。

次のように、ダンプとロードに json の代わりに pickle を使用するだけです。

    import json
    import pickle

    d = { 'field1': 'value1', 'field2': 2, }

    json.dump(d,open("testjson.txt","w"))

    print json.load(open("testjson.txt","r"))

    pickle.dump(d,open("testpickle.txt","w"))

    print pickle.load(open("testpickle.txt","r"))

生成される出力は次のとおりです (文字列と整数は正しく処理されます)。

    {u'field2': 2, u'field1': u'value1'}
    {'field2': 2, 'field1': 'value1'}

それで、私も同じ問題に遭遇しました。Google で最初に検索された結果は何だったでしょうか。

すべてのデータを PyGTK に渡す必要があるため、Unicode 文字列もあまり役に立ちません。そこで、別の再帰的変換方法を用意しました。実際には、タイプセーフな JSON 変換にも必要です。json.dump() は、Python オブジェクトなどの非リテラルを保護します。ただし、辞書インデックスは変換しません。

# removes any objects, turns unicode back into str
def filter_data(obj):
        if type(obj) in (int, float, str, bool):
                return obj
        elif type(obj) == unicode:
                return str(obj)
        elif type(obj) in (list, tuple, set):
                obj = list(obj)
                for i,v in enumerate(obj):
                        obj[i] = filter_data(v)
        elif type(obj) == dict:
                for i,v in obj.iteritems():
                        obj[i] = filter_data(v)
        else:
                print "invalid object in data, converting to string"
                obj = str(obj) 
        return obj

JSON dict を文字列として持っていました。キーと値は、次の例のような Unicode オブジェクトでした。

myStringDict = "{u'key':u'value'}"

を使うことができました byteify 文字列をに変換することで上記で提案された関数 dict オブジェクトを使用する ast.literal_eval(myStringDict).

フックを使用して Python2&3 をサポート (から https://stackoverflow.com/a/33571117/558397)

import requests
import six
from six import iteritems

requests.packages.urllib3.disable_warnings()  # @UndefinedVariable
r = requests.get("http://echo.jsontest.com/key/value/one/two/three", verify=False)

def _byteify(data):
    # if this is a unicode string, return its string representation
    if isinstance(data, six.string_types):
        return str(data.encode('utf-8').decode())

    # if this is a list of values, return list of byteified values
    if isinstance(data, list):
        return [ _byteify(item) for item in data ]

    # if this is a dictionary, return dictionary of byteified keys and values
    # but only if we haven't already byteified it
    if isinstance(data, dict):
        return {
            _byteify(key): _byteify(value) for key, value in iteritems(data)
        }
    # if it's anything else, return it in its original form
    return data

w = r.json(object_hook=_byteify)
print(w)

戻り値:

 {'three': '', 'key': 'value', 'one': 'two'}

今更ですが、再帰キャスターを作りました。私のニーズに応えており、比較的完成度が高いと思います。それはあなたを助けるかもしれません。

def _parseJSON(self, obj):
    newobj = {}

    for key, value in obj.iteritems():
        key = str(key)

        if isinstance(value, dict):
            newobj[key] = self._parseJSON(value)
        elif isinstance(value, list):
            if key not in newobj:
                newobj[key] = []
                for i in value:
                    newobj[key].append(self._parseJSON(i))
        elif isinstance(value, unicode):
            val = str(value)
            if val.isdigit():
                val = int(val)
            else:
                try:
                    val = float(val)
                except ValueError:
                    val = str(val)
            newobj[key] = val

    return newobj

次のように JSON オブジェクトを渡すだけです。

obj = json.loads(content, parse_float=float, parse_int=int)
obj = _parseJSON(obj)

私はこれをクラスのプライベートメンバーとして持っていますが、必要に応じてメソッドを再利用できます。

json オブジェクト自体が配列である場合 (私の使用例) を処理するために Wells の _parse_json() を書き直しました。

def _parseJSON(self, obj):
    if isinstance(obj, dict):
        newobj = {}
        for key, value in obj.iteritems():
            key = str(key)
            newobj[key] = self._parseJSON(value)
    elif isinstance(obj, list):
        newobj = []
        for value in obj:
            newobj.append(self._parseJSON(value))
    elif isinstance(obj, unicode):
        newobj = str(obj)
    else:
        newobj = obj
    return newobj

これは C で書かれた再帰的エンコーダーです。https://github.com/axiros/nested_encode

「平均的な」構造のパフォーマンス オーバーヘッドは、json.loads と比較して約 10% です。

python speed.py                                                                                            
  json loads            [0.16sec]: {u'a': [{u'b': [[1, 2, [u'\xd6ster..
  json loads + encoding [0.18sec]: {'a': [{'b': [[1, 2, ['\xc3\x96ster.
  time overhead in percent: 9%

このテスト構造を使用して:

import json, nested_encode, time

s = """
{
  "firstName": "Jos\\u0301",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "\\u00d6sterreich",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [],
  "spouse": null,
  "a": [{"b": [[1, 2, ["\\u00d6sterreich"]]]}]
}
"""


t1 = time.time()
for i in xrange(10000):
    u = json.loads(s)
dt_json = time.time() - t1

t1 = time.time()
for i in xrange(10000):
    b = nested_encode.encode_nested(json.loads(s))
dt_json_enc = time.time() - t1

print "json loads            [%.2fsec]: %s..." % (dt_json, str(u)[:20])
print "json loads + encoding [%.2fsec]: %s..." % (dt_json_enc, str(b)[:20])

print "time overhead in percent: %i%%"  % (100 * (dt_json_enc - dt_json)/dt_json)

チェックアウト これ このような同様の質問に対する答えは、

u- プレフィックスは、Unicode 文字列があることを意味します。実際に文字列を使用する場合、データには表示されません。印刷された出力に惑わされないでください。

たとえば、これを試してみてください。

print mail_accounts[0]["i"]

「u」は表示されません。

Python 3.6 でも、依然としてこの問題に遭遇することがあります。たとえば、REST API から応答を取得し、応答テキストを JSON にロードする場合、依然として Unicode 文字列を取得します。json.dumps() を使用した簡単な解決策が見つかりました。

response_message = json.loads(json.dumps(response.text))
print(response_message)

私もこの問題に遭遇し、JSON を扱う必要があったため、Unicode キーを文字列に変換する小さなループを思いつきました。(simplejson GAE では文字列キーが返されません。)

obj JSON からデコードされたオブジェクトです。

if NAME_CLASS_MAP.has_key(cls):
    kwargs = {}
    for i in obj.keys():
        kwargs[str(i)] = obj[i]
    o = NAME_CLASS_MAP[cls](**kwargs)
    o.save()

kwargs これは、GAEアプリケーションのコンストラクターに渡すものです(これは好きではありません) unicode キー入力 **kwargs)

Wells のソリューションほど堅牢ではありませんが、はるかに小型です。

からのコードを適応させました 答えマーク・アメリー, 、特に取り除くために isinstance アヒルタイピングのプロ向け。

エンコードは手動で行われ、 ensure_ascii 無効になっています。の Python ドキュメント json.dump そう言う

ensure_ascii が True (デフォルト) の場合、出力内のすべての非 ASCII 文字は \uXXXX シーケンスでエスケープされます。

免責事項:doctest ではハンガリー語を使用しました。ハンガリー語に関連する注目すべき文字エンコーディングは次のとおりです。 cp852 使用される IBM/OEM エンコーディング。DOS では (次のように呼ばれることもあります) アスキー, 、間違っていると思いますが、それはに依存しています コードページ 設定)、 cp1250 使用例:Windows では (次のように呼ばれることもあります) アンシ, 、ロケール設定に応じて)、および iso-8859-2, 、httpサーバーで時々使用されます。テストテキスト Tüskéshátú kígyóbűvölő に起因する コルタイ・ラスロー (ネイティブの個人名形式) の出身です ウィキペディア.

# coding: utf-8
"""
This file should be encoded correctly with utf-8.
"""
import json

def encode_items(input, encoding='utf-8'):
    u"""original from: https://stackoverflow.com/a/13101776/611007
    adapted by SO/u/611007 (20150623)
    >>> 
    >>> ## run this with `python -m doctest <this file>.py` from command line
    >>> 
    >>> txt = u"Tüskéshátú kígyóbűvölő"
    >>> txt2 = u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"
    >>> txt3 = u"uúuutifu"
    >>> txt4 = b'u\\xfauutifu'
    >>> # txt4 shouldn't be 'u\\xc3\\xbauutifu', string content needs double backslash for doctest:
    >>> assert u'\\u0102' not in b'u\\xfauutifu'.decode('cp1250')
    >>> txt4u = txt4.decode('cp1250')
    >>> assert txt4u == u'u\\xfauutifu', repr(txt4u)
    >>> txt5 = b"u\\xc3\\xbauutifu"
    >>> txt5u = txt5.decode('utf-8')
    >>> txt6 = u"u\\u251c\\u2551uutifu"
    >>> there_and_back_again = lambda t: encode_items(t, encoding='utf-8').decode('utf-8')
    >>> assert txt == there_and_back_again(txt)
    >>> assert txt == there_and_back_again(txt2)
    >>> assert txt3 == there_and_back_again(txt3)
    >>> assert txt3.encode('cp852') == there_and_back_again(txt4u).encode('cp852')
    >>> assert txt3 == txt4u,(txt3,txt4u)
    >>> assert txt3 == there_and_back_again(txt5)
    >>> assert txt3 == there_and_back_again(txt5u)
    >>> assert txt3 == there_and_back_again(txt4u)
    >>> assert txt3.encode('cp1250') == encode_items(txt4, encoding='utf-8')
    >>> assert txt3.encode('utf-8') == encode_items(txt5, encoding='utf-8')
    >>> assert txt2.encode('utf-8') == encode_items(txt, encoding='utf-8')
    >>> assert {'a':txt2.encode('utf-8')} == encode_items({'a':txt}, encoding='utf-8')
    >>> assert [txt2.encode('utf-8')] == encode_items([txt], encoding='utf-8')
    >>> assert [[txt2.encode('utf-8')]] == encode_items([[txt]], encoding='utf-8')
    >>> assert [{'a':txt2.encode('utf-8')}] == encode_items([{'a':txt}], encoding='utf-8')
    >>> assert {'b':{'a':txt2.encode('utf-8')}} == encode_items({'b':{'a':txt}}, encoding='utf-8')
    """
    try:
        input.iteritems
        return {encode_items(k): encode_items(v) for (k,v) in input.iteritems()}
    except AttributeError:
        if isinstance(input, unicode):
            return input.encode(encoding)
        elif isinstance(input, str):
            return input
        try:
            iter(input)
            return [encode_items(e) for e in input]
        except TypeError:
            return input

def alt_dumps(obj, **kwargs):
    """
    >>> alt_dumps({'a': u"T\\u00fcsk\\u00e9sh\\u00e1t\\u00fa k\\u00edgy\\u00f3b\\u0171v\\u00f6l\\u0151"})
    '{"a": "T\\xc3\\xbcsk\\xc3\\xa9sh\\xc3\\xa1t\\xc3\\xba k\\xc3\\xadgy\\xc3\\xb3b\\xc5\\xb1v\\xc3\\xb6l\\xc5\\x91"}'
    """
    if 'ensure_ascii' in kwargs:
        del kwargs['ensure_ascii']
    return json.dumps(encode_items(obj), ensure_ascii=False, **kwargs)

も強調したいと思います 答えジャレット・ハーディ これは、 JSON仕様, 、引用:

文字列は 0 個以上の Unicode 文字の集合です。

私のユースケースでは、json を含むファイルがありました。彼らです utf-8 エンコードされたファイル。 ensure_ascii その結果、適切にエスケープされますが、あまり読みにくい json ファイルが作成されます。そのため、Mark Amery の回答を私のニーズに合わせて調整しました。

doctest は特に考え抜かれたものではありませんが、誰かの役に立つことを願ってコードを共有します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top