Saya menggunakan Python 2 untuk mem-parsing JSON dari file teks yang disandikan ASCII .
Saat memuat file-file ini dengan salah satu json
atau simplejson
, semua nilai string saya dilemparkan ke objek Unicode, bukan objek string. Masalahnya adalah, saya harus menggunakan data dengan beberapa perpustakaan yang hanya menerima objek string. Saya tidak dapat mengubah perpustakaan atau memperbaruinya.
Apakah mungkin untuk mendapatkan objek string daripada objek Unicode?
Contoh
>>> 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`
Memperbarui
Pertanyaan ini sudah lama ditanyakan , ketika saya terjebak dengan Python 2 . Salah satu solusi mudah dan bersih untuk hari ini adalah dengan menggunakan versi Python terbaru - yaitu Python 3 dan seterusnya.
python
json
serialization
unicode
python-2.x
Brutus
sumber
sumber
str
Jawaban:
Solusi dengan
object_hook
Contoh penggunaan:
Bagaimana cara kerjanya dan mengapa saya menggunakannya?
Fungsi Mark Amery lebih pendek dan lebih jelas daripada yang ini, jadi apa gunanya? Mengapa Anda ingin menggunakannya?
Murni untuk kinerja . Jawaban Mark menerjemahkan teks JSON sepenuhnya terlebih dahulu dengan string unicode, kemudian berulang melalui seluruh nilai yang diterjemahkan untuk mengkonversi semua string ke string byte. Ini memiliki beberapa efek yang tidak diinginkan:
Jawaban ini mengurangi kedua masalah kinerja tersebut dengan menggunakan
object_hook
parameterjson.load
danjson.loads
. Dari dokumen :Karena kamus bersarang banyak level dalam kamus lain yang dilewati
object_hook
saat diterjemahkan , kita dapat mengubah byte atau daftar apa pun di dalamnya pada saat itu dan menghindari perlunya rekursi mendalam nantinya.Jawaban Markus tidak cocok untuk digunakan sebagaimana
object_hook
mestinya, karena berulang menjadi kamus bersarang. Kami mencegah rekursi dalam jawaban ini denganignore_dicts
parameter to_byteify
, yang diteruskan ke sana setiap saat kecuali saatobject_hook
melewatinya yang barudict
untuk byteify. Theignore_dicts
bendera mengatakan_byteify
untuk mengabaikandict
s karena mereka sudah byteified.Akhirnya, implementasi
json_load_byteified
danjson_loads_byteified
panggilan kami_byteify
(denganignore_dicts=True
) pada hasil yang dikembalikan darijson.load
ataujson.loads
untuk menangani kasus di mana teks JSON yang diterjemahkan tidak memilikidict
di tingkat atas.sumber
return { byteify(key, ignore_dicts=True): _byteify(value, ignore_dicts=True) for key, value in data.iteritems() }
denganreturn dict((_byteify(key, ignore_dicts=True), _byteify(value, ignore_dicts=True)) for key, value in data.iteritems())
untuk itu untuk bekerja.json_loads_byteified('[' * 990 + ']' * 990)
. Dengan 991 crash. Mark masih bekerja dengan 991:byteify(json.loads('[' * 991 + ']' * 991))
. Itu crash di 992. Jadi setidaknya dalam tes ini, Mark bisa lebih dalam, bertentangan dengan apa yang Anda katakan.Meskipun ada beberapa jawaban yang baik di sini, saya akhirnya menggunakan PyYAML untuk mengurai file JSON saya, karena memberikan kunci dan nilai sebagai
str
string tipe bukanunicode
tipe. Karena JSON adalah bagian dari YAML berfungsi dengan baik:Catatan
Beberapa hal yang perlu diperhatikan:
Saya mendapatkan objek string karena semua entri saya dikodekan ASCII . Jika saya akan menggunakan entri yang disandikan unicode, saya akan mendapatkannya kembali sebagai objek unicode - tidak ada konversi!
Anda harus (mungkin selalu) menggunakan
safe_load
fungsi PyYAML ; jika Anda menggunakannya untuk memuat file JSON, Anda tidak memerlukan "kekuatan tambahan" dariload
fungsi tersebut.Jika Anda menginginkan parser YAML yang memiliki lebih banyak dukungan untuk versi 1.2 dari spesifikasi (dan dengan benar mem-parsing angka yang sangat rendah ) coba Ruamel YAML :
pip install ruamel.yaml
dan hanyaimport ruamel.yaml as yaml
itu yang saya butuhkan dalam pengujian saya.Konversi
Seperti yang dinyatakan, tidak ada konversi! Jika Anda tidak yakin untuk hanya berurusan dengan nilai-nilai ASCII (dan Anda tidak bisa memastikan sebagian besar waktu), lebih baik gunakan fungsi konversi :
Saya menggunakan yang dari Mark Amery beberapa kali sekarang, ini berfungsi dengan baik dan sangat mudah digunakan. Anda juga dapat menggunakan fungsi yang sama sebagai
object_hook
gantinya, karena dapat meningkatkan kinerja Anda pada file besar. Lihat jawaban yang sedikit lebih terlibat dari Mirec Miskuf untuk itu.sumber
yaml.load(json.dumps([u'a', u'£', u'É']))
di shell Python dan amati bahwa Anda kembali['a', u'\xa3', u'\xc9']
(yang berisiunicode
string). Jika Anda tidak dapat memastikan bahwa data Anda hanya berisi karakter dari rangkaian karakter ASCII, Anda harus menggunakan pendekatan yang berbeda (saya sarankan jawaban saya sendiri).[u'a', u'b']
hati-hati.Tidak ada opsi bawaan untuk membuat fungsi modul json mengembalikan string byte alih-alih string unicode. Namun, fungsi rekursif singkat dan sederhana ini akan mengubah objek JSON yang didekode dari menggunakan string unicode menjadi string byte UTF-8-encoded:
Sebut saja ini pada output yang Anda dapatkan dari
json.load
ataujson.loads
panggilan.Beberapa catatan:
return {byteify(key): byteify(value) for key, value in input.iteritems()}
denganreturn dict([(byteify(key), byteify(value)) for key, value in input.iteritems()])
, karena pemahaman kamus tidak didukung hingga Python 2.7.object_hook
atauobject_pairs_hook
. Sejauh ini jawaban Mirec Miskuf adalah satu-satunya yang berhasil melakukan ini dengan benar, meskipun sebagai konsekuensinya, secara signifikan lebih rumit daripada pendekatan saya.sumber
object_hook
sebenarnya jauh lebih buruk daripada yang ini, tetapi, menggunakanobject_pairs_hook
, Anda dapat menemukan metode yang cukup efisien yang tidak memerlukan rekursi atau meninjau kembali node yang tidak mengandung string.object_pairs_hook
Metode ini mungkin sangat sedikit lebih sulit untuk memahami dari satu ini (Anda perlu memahami cara kerja parameter dan mengapa daftar dan dicts membutuhkan penanganan yang berbeda), dan manfaat kinerja akan tidak peduli untuk kebanyakan orang ... tapi aku harapkan itu ada, terutama bagi siapa pun yang berurusan dengan objek JSON bersarang sangat luar biasa.Anda dapat menggunakan
object_hook
parameter untukjson.loads
lewat dalam konverter. Anda tidak perlu melakukan konversi setelah faktanya. Thejson
modul akan selalu melewatiobject_hook
dicts saja, dan secara rekursif akan lulus dalam dicts bersarang, sehingga Anda tidak perlu recurse ke dicts bersarang sendiri. Saya tidak berpikir saya akan mengubah string unicode ke angka seperti yang ditunjukkan Wells. Jika itu adalah string unicode, itu dikutip sebagai string dalam file JSON, jadi itu seharusnya string (atau file tersebut buruk).Juga, saya akan mencoba untuk menghindari melakukan sesuatu seperti
str(val)
pada suatuunicode
objek. Anda harus menggunakanvalue.encode(encoding)
dengan penyandian yang valid, tergantung pada apa yang diharapkan oleh lib eksternal Anda.Jadi, misalnya:
sumber
s
adalah JSONObject
(kumpulan kunci unordered: nilai berpasangan dengan karakter ':' yang memisahkan kunci dan nilainya, dipisahkan dengan koma dan tertutup dalam kurung kurawal), tetapi tidak jika itu, katakanlah, a JSONArray
. Jadi jika diberikanArray
seperti JSON["a", "b"]
, hasilnya akan tetap[u'a', u'b']
. Tak satu pun dari parameter hook-type kustomisasi lain yang tersedia saat ini untukjson.loads()
dapat melakukan pekerjaan dengan baik.json
modul akan secara berulang melewati nesteddict
s, tidak perlu memeriksa mereka di dua fungsi - jadi duaelif
klausa yang memeriksa mereka harus dihapus.from Utility import *
, fungsi tidak akan terlihat karena garis bawah itu.object_hook
dipanggil untuk setiap objek json yang diuraikan, jadi jika Anda kembali ke apa yang diberikan kepada Anda, Anda mengulangi "byteifikasi" hal-hal yang telah Anda "byteified". Kinerja akan tumbuh secara geometris dengan ukuran objek. Saya telah menyertakan jawaban di sini yang menggunakanobject_pairs_hook
dan tidak menderita masalah itu.Itu karena json tidak memiliki perbedaan antara objek string dan objek unicode. Mereka semua adalah string dalam javascript.
Saya pikir JSON benar untuk mengembalikan objek unicode . Sebenarnya, saya tidak akan menerima apa pun yang kurang, karena string javascript sebenarnya adalah
unicode
objek (yaitu string JSON (javascript) dapat menyimpan segala jenis karakter unicode) sehingga masuk akal untuk membuatunicode
objek saat menerjemahkan string dari JSON. String biasa tidak cocok karena perpustakaan harus menebak pengodean yang Anda inginkan.Lebih baik menggunakan
unicode
objek string di mana-mana. Jadi pilihan terbaik Anda adalah memperbarui perpustakaan Anda sehingga mereka dapat menangani objek unicode.Tetapi jika Anda benar-benar ingin bytestrings, cukup enkode hasilnya ke enkode pilihan Anda:
sumber
Ada solusi yang mudah.
TL; DR - Gunakan
ast.literal_eval()
sebagai gantijson.loads()
. Keduanyaast
danjson
berada di perpustakaan standar.Meskipun bukan jawaban 'sempurna', ada satu yang cukup jauh jika rencana Anda mengabaikan Unicode sama sekali. Dalam Python 2.7
memberi:
Ini menjadi lebih berbulu ketika beberapa objek benar-benar string Unicode. Jawaban lengkap menjadi cepat berbulu.
sumber
null
,true
ataufalse
nilai-nilai, karena mereka tidak berlaku di python dan akan menyebabkanliteral_eval()
gagal.\/
) di dalam string, atau urutan escape unicode (seperti"\u0061"
, yang merupakan cara penulisan lain"a"
). Sintaks literal Python tidak kompatibel dengan JSON dalam beberapa hal, dan saya tidak akan mempercayai jawaban ini untuk skrip apa pun yang tidak akan saya buang.json
untuk membuang data, gunakan sajaprint
jika menjalankan python. Kemudianast.literal_eval
bekerjaJawaban Mike Brennan dekat, tetapi tidak ada alasan untuk menelusuri ulang seluruh struktur. Jika Anda menggunakan
object_hook_pairs
parameter (Python 2.7+):Dengan itu, Anda mendapatkan setiap objek JSON yang diserahkan kepada Anda, sehingga Anda dapat melakukan decoding tanpa perlu rekursi:
Perhatikan bahwa saya tidak pernah harus memanggil kait secara berulang karena setiap objek akan diserahkan ke kait ketika Anda menggunakan
object_pairs_hook
. Anda memang harus memperhatikan daftar, tetapi seperti yang Anda lihat, objek dalam daftar akan dikonversi dengan benar, dan Anda tidak perlu berulang untuk mewujudkannya.EDIT: Seorang rekan kerja menunjukkan bahwa Python2.6 tidak punya
object_hook_pairs
. Anda masih bisa menggunakan ini akan Python2.6 dengan membuat perubahan yang sangat kecil. Pada kait di atas, ubah:untuk
Kemudian gunakan
object_hook
sebagai gantiobject_pairs_hook
:Menggunakan
object_pairs_hook
hasil dalam satu kamus kurang sedang dipakai untuk setiap objek di objek JSON, yang, jika Anda parsing dokumen besar, mungkin bernilai sementara.sumber
deunicodify_hook
yang Anda tunjukkan dalam jawaban ini? Saat ini, Anda memiliki implementasideunicodify_hook
yang tidak mengulangi daftar dan membatalkan tanda string dan daftar di dalamnya, dan dengan demikian output yang Anda tampilkan tidak cocok dengan output yang benar-benar dihasilkan oleh hook Anda. Perbaiki itu, dan jawaban ini akan lebih unggul dari saya.object_pairs_hook
hanya dipanggil untuk objek , jika teks JSON Anda memiliki daftar string di tingkat atas, solusi ini akan gagal. Tidak ada cara untuk memperbaikinya tanpa memanggil beberapa fungsi dari benda yang dikembalikanjson.load
; tidak adajson.load
kait yang dapat menjamin Anda dapat menangani setiap string. Saya pikir ini adalah kesalahan yang cukup besar bagi saya untuk terus merekomendasikan solusi saya menggunakan kait.Saya khawatir tidak ada cara untuk mencapai ini secara otomatis dalam perpustakaan simplejson.
Pemindai dan dekoder di simplejson dirancang untuk menghasilkan teks unicode. Untuk melakukan ini, perpustakaan menggunakan fungsi yang disebut
c_scanstring
(jika tersedia, untuk kecepatan), ataupy_scanstring
jika versi C tidak tersedia. Thescanstring
fungsi disebut beberapa kali oleh hampir setiap rutinitas yang simplejson memiliki untuk decoding struktur yang mungkin berisi teks. Anda harus melakukan monkeypatchscanstring
nilai dalam simplejson.decoder, atau subclassJSONDecoder
dan memberikan cukup banyak seluruh implementasi Anda sendiri dari apa pun yang mungkin berisi teks.Alasan bahwa simplejson mengeluarkan unicode, adalah, karena spesifikasi json menyebutkan bahwa "string adalah kumpulan dari nol atau lebih karakter Unicode" ... dukungan untuk unicode dianggap sebagai bagian dari format itu sendiri. Implementasi Simplejson
scanstring
sejauh ini untuk memindai dan menafsirkan unicode escapes (bahkan pengecekan kesalahan untuk representasi charset multi-byte yang salah), sehingga satu-satunya cara andal dapat mengembalikan nilai kepada Anda adalah sebagai unicode.Jika Anda memiliki perpustakaan lama yang membutuhkan
str
, saya sarankan Anda dengan susah payah mencari struktur data bersarang setelah penguraian (yang saya akui adalah apa yang secara eksplisit Anda katakan ingin Anda hindari ... maaf), atau mungkin bungkus perpustakaan Anda dalam semacam fasad tempat Anda dapat memijat parameter input pada tingkat yang lebih terperinci. Pendekatan kedua mungkin lebih mudah dikelola daripada yang pertama jika struktur data Anda benar-benar bersarang.sumber
Sebagai Mark (Amery) dengan benar mencatat: Menggunakan deserializer PyYaml pada json dump hanya berfungsi jika Anda memiliki ASCII saja. Setidaknya di luar kotak.
Dua komentar cepat tentang pendekatan PyYaml:
JANGAN PERNAH menggunakan yaml.load data dari lapangan. Ini adalah fitur (!) Dari yaml untuk mengeksekusi kode arbitrer yang tersembunyi dalam struktur.
Anda dapat membuatnya berfungsi juga untuk non ASCII melalui ini:
Namun kinerja tidak ada bedanya dengan jawaban Mark Amery:
Melemparkan beberapa dict sampel yang sangat bersarang ke dua metode, saya mendapatkan ini (dengan dt [j] = waktu delta json.loads (json.dumps (m))):
Jadi deserialisasi termasuk sepenuhnya berjalan pohon dan pengkodean, baik dalam urutan besarnya implementasi berbasis json C. Saya menemukan ini sangat cepat dan juga lebih kuat daripada beban yaml di struktur yang sangat bersarang. Dan rawan kesalahan keamanan, melihat yaml.load.
=> Sementara saya akan menghargai pointer ke konverter hanya berbasis C fungsi byteify harus menjadi jawaban default.
Ini berlaku terutama jika struktur json Anda berasal dari bidang, yang berisi input pengguna. Karena itu, Anda mungkin harus tetap berjalan di atas struktur Anda - independen pada struktur data internal yang Anda inginkan ('unicode sandwich' atau string byte saja).
Mengapa?
Normalisasi Unicode . Untuk yang tidak sadar: Ambil obat penghilang rasa sakit dan baca ini .
Jadi menggunakan rekursi byteify Anda membunuh dua burung dengan satu batu:
Dalam tes saya ternyata mengganti input.encode ('utf-8') dengan unicodedata.normalisasi ('NFC', input) .encode ('utf-8') bahkan lebih cepat daripada tanpa NFC - tetapi itu sangat tergantung pada data sampel saya kira.
sumber
Gotcha adalah itu
simplejson
danjson
dua modul yang berbeda, setidaknya dalam cara mereka menangani unicode. Anda memilikijson
di py 2.6+, dan ini memberi Anda nilai unicode, sedangkansimplejson
mengembalikan objek string. Coba saja easy_install-ing simplejson di lingkungan Anda dan lihat apakah itu berhasil. Itu untuk saya.sumber
Cukup gunakan acar daripada json untuk dump dan memuat, seperti:
Output yang dihasilkannya adalah (string dan integer ditangani dengan benar):
sumber
safe_load
di YAML, saya tidak tahu apakah ada yang serupa untuk acar .Jadi, saya mengalami masalah yang sama. Coba tebak apa hasil Google pertama.
Karena saya harus meneruskan semua data ke PyGTK, string unicode juga tidak berguna bagi saya. Jadi saya punya metode konversi rekursif lain. Ini sebenarnya juga diperlukan untuk konversi JSON typesafe - json.dump () akan menjamin pada non-literal, seperti objek Python. Tidak mengonversi indeks dict.
sumber
Saya memiliki dict JSON sebagai string. Kunci dan nilai adalah objek unicode seperti pada contoh berikut:
Saya bisa menggunakan
byteify
fungsi yang disarankan di atas dengan mengubah string kedict
objek menggunakanast.literal_eval(myStringDict)
.sumber
{u'key':u'value'}
bukan JSON.Mendukung Python2 & 3 menggunakan kait (dari https://stackoverflow.com/a/33571117/558397 )
Pengembalian:
sumber
Ini sudah terlambat dari permainan, tetapi saya membangun kastor rekursif ini. Ini berfungsi untuk kebutuhan saya dan saya pikir itu relatif lengkap. Ini dapat membantu Anda.
Cukup berikan objek JSON seperti ini:
Saya memilikinya sebagai anggota pribadi kelas, tetapi Anda dapat menggunakan kembali metode yang Anda inginkan.
sumber
json.loads
panggilan diperlukan terlebih dahulu), secara sewenang-wenang mencoba mengubah string menjadi int tanpa alasan yang jelas, dan tidak menyalin-dan- tempel siap.Saya menulis ulang Wells _parse_json () untuk menangani kasus-kasus di mana objek json itu sendiri adalah array (kasus penggunaan saya).
sumber
di sini adalah encoder rekursif yang ditulis dalam C: https://github.com/axiros/nested_encode
Overhead kinerja untuk struktur "rata-rata" sekitar 10% dibandingkan dengan json.loads.
menggunakan struktur tes ini:
sumber
Dengan Python 3.6, terkadang saya masih mengalami masalah ini. Misalnya, ketika mendapatkan respons dari REST API dan memuat teks respons ke JSON, saya masih mendapatkan string unicode. Menemukan solusi sederhana menggunakan json.dumps ().
sumber
Saya mengalami masalah ini juga, dan karena harus berurusan dengan JSON, saya membuat loop kecil yang mengubah kunci unicode menjadi string. (
simplejson
pada GAE tidak mengembalikan kunci string.)obj
adalah objek yang diterjemahkan dari JSON:kwargs
adalah apa yang saya sampaikan ke konstruktor aplikasi GAE (yang tidak sukaunicode
kunci**kwargs
)Tidak sekuat solusi dari Wells, tetapi jauh lebih kecil.
sumber
Saya sudah diadaptasi kode dari jawaban dari Mark Amery , terutama untuk menyingkirkan
isinstance
untuk pro bebek-mengetik.Pengkodean dilakukan secara manual dan
ensure_ascii
dinonaktifkan. Python docs forjson.dump
mengatakan ituPenafian: dalam dokumen saya menggunakan bahasa Hongaria. Beberapa pengkodean karakter terkait Hongaria yang terkenal adalah: pengkodean
cp852
IBM / OEM yang digunakan misalnya. di DOS (kadang-kadang disebut ascii , salah saya pikir, itu tergantung pada pengaturan codepage ),cp1250
misalnya digunakan. di Windows (kadang-kadang disebut sebagai ansi , tergantung pada pengaturan lokal), daniso-8859-2
, kadang-kadang digunakan pada server http. Teks tesTüskéshátú kígyóbűvölő
dikaitkan dengan Koltai László (bentuk nama pribadi asli) dan dari wikipedia .Saya juga ingin menyoroti jawaban dari Jarret Hardie yang referensi yang JSON spesifikasi , mengutip:
Dalam kasus penggunaan saya, saya punya file dengan json. Mereka adalah
utf-8
file yang disandikan.ensure_ascii
hasil dalam file json yang lolos dengan benar tetapi tidak terlalu mudah dibaca, itulah sebabnya saya mengadaptasi jawaban Mark Amery agar sesuai dengan kebutuhan saya.Doctest tidak terlalu bijaksana tetapi saya membagikan kode dengan harapan akan bermanfaat bagi seseorang.
sumber
json.loads
akan berupa daftar atau dikte, bukan tipe yang ditentukan pengguna atau yang ditentukan perpustakaan yang mengimplementasikan metode dan metode sihir mereka, jadi mengapa tidak hanya melakukanisinstance
pemeriksaan? Bukankah itu lebih mudah dipahami daripada memeriksa keberadaaniteritems
atau apakahiter
akan menerima objek sebagai argumen?Lihat jawaban ini untuk pertanyaan serupa seperti ini yang menyatakan itu
Awalan u- artinya Anda memiliki string Unicode. Ketika Anda benar-benar menggunakan string, itu tidak akan muncul di data Anda. Jangan terlempar oleh hasil cetak.
Misalnya, coba ini:
Anda tidak akan melihat Anda.
sumber
'{}'.format({u'x' : u'y'})
masih termasuk u.