私はPython 2を使ってASCIIエンコードされたテキストファイルからJSONをパースします。
これらのファイルを json
または simplejson
のいずれかでロードすると、私の文字列値はすべて文字列オブジェクトではなくUnicodeオブジェクトにキャストされます。問題は、文字列オブジェクトだけを受け付けるライブラリでデータを使用しなければならないことです。 Iはライブラリの変更も更新もできません。
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`
私がPython 2で立ち往生していたときに、この質問はずっと前にされました。今日のための1つの簡単できれいな解決策は最近のバージョンのPython、すなわちPython 3を使うことである。
ここでは良い答えがいくつかありますが、JSONファイルを解析するために PyYAML を使用しました。キーと値がstr
name__型ではなくunicode
name__型の文字列として渡されるためです。 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エンコードされたエントリを使うのなら、それらをUnicodeオブジェクトとして返します - 変換はありません!
あなたは(おそらくいつも)PyYAMLのsafe_load
関数を使うべきです。 JSONファイルをロードするためにそれを使用する場合は、とにかくload
name__関数の「追加の機能」は必要ありません。
仕様の1.2バージョンをもっとサポートするYAMLパーサが欲しいなら(そして 正しく少数の数値を解析する )試してみてください Ruamel YAML :pip install ruamel.yaml
とimport ruamel.yaml as yaml
だけが必要でした私のテストで。
述べたように、変換はありません! ASCIIの値だけを扱うのが確実でない場合(そしてほとんどの場合、確実に判断できない場合)は、変換関数を使用してください:
私は Mark Amery のものを今から2、3回使用しましたが、うまく機能してとても使いやすいです。大きなファイルのパフォーマンスが向上する可能性があるため、代わりにobject_hook
と同様の関数を使用することもできます。もう少し複雑な Mirec Miskufからの回答 を見てください。
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
呼び出しから得られる出力でこれを呼び出すだけです。
いくつかのメモ:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
をreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
に置き換えます。object_hook
またはobject_pairs_hook
パラメータを慎重に使用することで回避できる、望ましくないパフォーマンス特性がいくつかあります。 Mirec Miskufの答え これは、これを正しく実現することができる唯一のものですが、結果として、それは私のアプローチよりもかなり複雑です。コンバーターを渡すには、 object_hook
にjson.loads
パラメーターを使用できます。あなたは事実の後に変換をする必要はありません。 json
モジュールは常にobject_hook
辞書のみを渡し、それは再帰的に入れ子辞書を渡しますので、自分で入れ子辞書に再帰する必要はありません。 Wellsが示すように、Unicode文字列を数値に変換するとは思わない。それがUnicode文字列である場合、それはJSONファイルで文字列として引用されていたので、それは文字列であることが想定されています(またはファイルが不良です)。
また、unicode
オブジェクトに対してstr(val)
のようなことをしないようにします。あなたの外部ライブラリが何を期待しているかに応じて、あなたは有効なエンコーディングで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オブジェクトを返すのが正しいですだと思います。 JavaScriptの文字列実際にはunicode
のオブジェクトです(つまりJSON(javascript)の文字列はany kindを格納できます)そのため、JSONから文字列を変換するときにunicode
オブジェクトを作成することは意味があります。ライブラリはあなたが望むエンコーディングを推測しなければならないので、プレーンな文字列はちょうど収まりません。
どこでも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文字列であるとき、これはより毛深いです。完全な答えはすぐに毛深いです。
Mike Brennan's answer は近いですが、構造全体を行き来する理由はありません。 object_hook_pairs
(Python 2.7+)パラメータを使用すると、
object_pairs_hook
は、ペアの順序付きリストでデコードされたオブジェクトリテラルの結果とともに呼び出されるオプションの関数です。dict
の代わりにobject_pairs_hook
の戻り値が使用されます。この機能は、キーと値のペアがデコードされる順序に依存するカスタムデコーダを実装するために使用できます(たとえば、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
(利用可能であれば高速化)、またはCバージョンが利用できない場合はpy_scanstring
という関数を使用します。 scanstring
関数は、テキストを含む可能性のある構造体をデコードするためにsimplejsonが持っているほぼすべてのルーチンによって、数回呼び出されます。あなたはsimplejson.decoderの中のscanstring
の値をサブタイプするか、JSONDecoder
をサブクラス化して、テキストを含んでいるかもしれないものすべてのあなた自身の完全な実装を提供する必要があります。
しかしながら、simplejsonがUnicodeを出力するのは、 json spec が "文字列は0個以上のUnicode文字の集合"であると具体的に述べているためです... Unicodeのサポートはフォーマットの一部として想定されます自体。 Simplejsonのscanstring
の実装は、Unicodeのエスケープをスキャンして解釈する(不正なマルチバイト文字セット表現のエラーチェックでさえも)ため、確実に値をUnicodeに返すことができる唯一の方法です。
str
を必要とする古いライブラリを持っているのであれば、構文解析後にネストされたデータ構造を面倒に検索するか(明示的に避けたいと言っていることです...ごめんなさい)よりきめ細かいレベルで入力パラメータをマッサージできるファサードの。データ構造が実際に深くネストされている場合、2番目のアプローチは最初のアプローチよりも管理しやすいかもしれません。
Mark(Amery)が正しく記しているように、jsonダンプでPyYamlのデシリアライザを使用するのは、ASCIIがある場合にのみ有効です。少なくとも箱から出して。
PyYamlアプローチについての2つの簡単なコメント:
NEVER フィールドからのデータにyaml.loadを使用します。構造内に隠された任意のコードを実行するためのyamlのその機能(!)。
これによってcanを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]
そのため、ツリーのandエンコーディングを完全にウォーキングすることを含む、直列化復元は、jsonのCベースの実装の桁の範囲内です。深くネストされた構造体では、これは驚くほど高速で、またその負荷も山積みの負荷よりも堅牢です。そしてyaml.loadを見て、セキュリティエラーが起こりにくくなりました。
=> Cベースのコンバーターへのポインターをいただければ幸いですが、byteify関数がデフォルトの答えになるはずです。
これは、json構造体がユーザー入力を含むフィールドからのものである場合に特に当てはまります。なぜならあなたはおそらくあなたの構造体の上をとにかく歩く必要があるからです - あなたの望む内部データ構造体( 'unicode sandwich'またはバイト文字列のみ)から独立しています。
どうして?
Unicode正規化知らない人のために:鎮痛剤を飲んで this と読んでください。
そのため、byteify再帰を使用して、1つの石で2羽の鳥を殺します。
私のテストでは、input.encode( 'utf-8')をunicodedata.normalize( 'NFC'、input).encode( 'utf-8')に置き換えるほうがw/o NFC - しかし、それは私が推測するサンプルデータに大きく依存しています。
simplejson
とjson
は、少なくともユニコードを扱う方法では、2つの異なるモジュールです。あなたはpy 2.6+でjson
を持っています、そしてこれはあなたにunicode値を与えますが、simplejson
は文字列オブジェクトを返します。あなたの環境でeasy_install-ing simplejsonを試して、それがうまくいくかどうか確かめてください。それは私のためにしました。
次のように、ダンプとロードには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'}
フックを使用してPython 2と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'}
だから、私は同じ問題に遭遇しました。最初の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オブジェクト自身が配列であるケースを扱うために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
これはゲームに遅れていますが、私はこの再帰キャスターを作りました。それは私の必要性のために働きます、そして、私はそれが比較的完全であると思います。それはあなたを助けるかもしれません。
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辞書を持っていました。キーと値は、次の例のようにUnicodeオブジェクトです。
myStringDict = "{u'key':u'value'}"
ast.literal_eval(myStringDict)
を使用して文字列をbyteify
オブジェクトに変換することで、上で提案したdict
関数を使用できます。
チェックアウト this これと似たような同様の質問への答え
接頭辞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)
これは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)