Saya ingin membaca beberapa objek JSON dari file / stream dengan Python, satu per satu. Sayangnya json.load()
hanya .read()
sampai akhir file; sepertinya tidak ada cara untuk menggunakannya untuk membaca satu objek atau dengan malas mengulanginya di atas objek.
Apakah ada cara untuk melakukan ini? Menggunakan pustaka standar akan ideal, tetapi jika ada pustaka pihak ketiga, saya akan menggunakannya sebagai gantinya.
Saat ini saya meletakkan setiap objek pada baris terpisah dan menggunakan json.loads(f.readline())
, tetapi saya benar-benar memilih untuk tidak melakukan ini.
Contoh Penggunaan
example.py
import my_json as json
import sys
for o in json.iterload(sys.stdin):
print("Working on a", type(o))
in.txt
{"foo": ["bar", "baz"]} 1 2 [] 4 5 6
sesi contoh
$ python3.2 example.py < in.txt
Working on a dict
Working on a int
Working on a int
Working on a list
Working on a int
Working on a int
Working on a int
python
json
serialization
Jeremy
sumber
sumber
{"foo": ["bar", "baz"]}
dalam contoh saya), itu harusyield
itu dan kemudian melanjutkan ke yang berikutnya (1
).'\n'
(satu baris baru, bukan dua karakter) dalam representasi jsonnya karena'\n'
harus di-escape di dalam string json dan oleh karena itu'\n'
dapat digunakan untuk pemformatan hanya misalnya, saya percayajson.dumps()
tidak ' t perkenalkan'\n'
secara default. Berhati-hatilah karena baris baru Unicode seperti U + 0085 mungkin terhapus di dalam string json.Jawaban:
Inilah solusi yang jauh lebih sederhana. Rahasianya adalah mencoba, gagal, dan menggunakan informasi dalam pengecualian untuk mengurai dengan benar. Satu-satunya batasan adalah file harus dapat dicari.
Edit: baru saja perhatikan bahwa ini hanya akan berfungsi untuk Python> = 3.5. Untuk sebelumnya, kegagalan mengembalikan ValueError, dan Anda harus mengurai posisi dari string, misalnya
sumber
re
tidak akan berhasil - garis miring terbalik harus dilepaskan. Pertimbangkan string mentahr'...'
.ujson
alih-alihjson
Anda akan mendapatkan percepatan besarJSON umumnya tidak terlalu baik untuk penggunaan bertahap semacam ini; tidak ada cara standar untuk membuat serialisasi banyak objek sehingga dapat dengan mudah dimuat satu per satu, tanpa mengurai seluruhnya.
Solusi objek per baris yang Anda gunakan juga terlihat di tempat lain. Scrapy menyebutnya 'garis JSON':
Anda dapat melakukannya sedikit lebih banyak secara Python:
Saya pikir ini tentang cara terbaik - tidak bergantung pada perpustakaan pihak ketiga mana pun, dan mudah untuk memahami apa yang sedang terjadi. Saya telah menggunakannya di beberapa kode saya juga.
sumber
Mungkin agak terlambat, tetapi saya memiliki masalah yang sama persis (yah, kurang lebih). Solusi standar saya untuk masalah ini biasanya hanya melakukan pemisahan regex pada beberapa objek root yang terkenal, tetapi dalam kasus saya itu tidak mungkin. Satu-satunya cara yang layak untuk melakukan ini secara umum adalah dengan mengimplementasikan tokenizer yang tepat .
Setelah tidak menemukan solusi yang cukup umum dan berkinerja cukup baik, saya akhirnya melakukan ini sendiri, menulis
splitstream
modul. Ini adalah pra-tokenizer yang memahami JSON dan XML dan membagi aliran berkelanjutan menjadi beberapa bagian untuk penguraian (meskipun demikian, penguraian yang sebenarnya terserah Anda). Untuk mendapatkan beberapa jenis kinerja darinya, itu ditulis sebagai modul C.Contoh:
sumber
Tentu kamu bisa melakukan ini. Anda hanya perlu mengambilnya
raw_decode
secara langsung. Implementasi ini memuat seluruh file ke dalam memori dan beroperasi pada string itu (sepertijson.load
halnya); jika Anda memiliki file besar, Anda dapat memodifikasinya menjadi hanya membaca dari file seperlunya tanpa banyak kesulitan.Penggunaan: seperti yang Anda minta, ini adalah generator.
sumber
Ini adalah masalah yang cukup buruk sebenarnya karena Anda harus melakukan streaming dalam garis, tetapi pola cocok di beberapa garis dengan tanda kurung, tetapi juga pola yang cocok json. Ini semacam json-preparse diikuti oleh json parse. Json, dibandingkan dengan format lain, mudah diurai sehingga tidak selalu perlu menggunakan parsing library, namun, bagaimana cara kami menyelesaikan masalah yang bertentangan ini?
Generator untuk menyelamatkan!
Keindahan generator untuk masalah seperti ini adalah Anda dapat menumpuknya satu sama lain secara bertahap menghilangkan kesulitan masalah sambil mempertahankan kemalasan. Saya juga mempertimbangkan untuk menggunakan mekanisme untuk mengirimkan kembali nilai ke generator (send ()) tetapi untungnya ternyata saya tidak perlu menggunakannya.
Untuk mengatasi masalah pertama, Anda memerlukan semacam streamingfinditer, sebagai versi streaming dari re.finditer. Upaya saya di bawah ini menarik baris sesuai kebutuhan (hapus komentar pernyataan debug untuk melihat) sementara masih mengembalikan pertandingan. Saya benar-benar kemudian memodifikasinya sedikit untuk menghasilkan garis yang tidak cocok serta kecocokan (ditandai sebagai 0 atau 1 di bagian pertama dari tupel yang dihasilkan).
Dengan itu, maka dimungkinkan untuk mencocokkan hingga kurung kurawal, memperhitungkan setiap kali apakah kurung siku seimbang, dan kemudian mengembalikan objek sederhana atau gabungan yang sesuai.
Ini mengembalikan tupel sebagai berikut:
Pada dasarnya itulah bagian buruk yang dilakukan. Kita sekarang hanya perlu melakukan tingkat akhir penguraian sesuai keinginan kita. Misalnya kita dapat menggunakan fungsi iterload Jeremy Roman (Terima kasih!) Untuk melakukan parsing untuk satu baris:
Menguji:
Saya mendapatkan hasil ini (dan jika Anda mengaktifkan baris debug itu, Anda akan melihatnya menarik baris sesuai kebutuhan):
Ini tidak akan berhasil untuk semua situasi. Karena penerapan
json
pustaka, mustahil untuk bekerja sepenuhnya dengan benar tanpa menerapkan ulang parser sendiri.sumber
"}"
dan"]"
terjadi di dalam string JSON? Saya rasa ini adalah batasan umum parsing dengan regex.Saya yakin cara yang lebih baik untuk melakukannya adalah dengan menggunakan mesin negara. Di bawah ini adalah contoh kode yang saya kerjakan dengan mengonversi kode NodeJS pada tautan di bawah ini ke Python
3 (menggunakan kata kunci nonlokal hanya tersedia di Python 3, kode tidak akan berfungsi pada Python 2)Edit-1: Memperbarui dan membuat kode kompatibel dengan Python 2
Edit-2: Memperbarui dan menambahkan versi hanya Python3 juga
https://gist.github.com/creationix/5992451
Versi hanya Python 3
Versi yang kompatibel dengan Python 2
Mengujinya
Outputnya adalah
sumber
Saya ingin memberikan solusi. Pikiran utamanya adalah "mencoba" untuk memecahkan kode: jika gagal, berikan lebih banyak umpan, jika tidak gunakan informasi offset untuk mempersiapkan pendekodean berikutnya.
Namun modul json saat ini tidak dapat mentolerir SPACE di head of string untuk didekodekan, jadi saya harus melepasnya.
========================= Saya telah menguji beberapa file txt, dan berfungsi dengan baik. (in1.txt)
(in2.txt)
(in.txt, inisial Anda)
(keluaran untuk testcase Benedict)
sumber
Ini milik saya:
sumber
Saya menggunakan solusi elegan @ wuilang. Pendekatan sederhana - membaca satu byte, mencoba memecahkan kode, membaca satu byte, mencoba memecahkan kode, ... - berhasil, tetapi sayangnya itu sangat lambat.
Dalam kasus saya, saya mencoba membaca objek JSON yang "dicetak dengan cantik" dari jenis objek yang sama dari sebuah file. Ini memungkinkan saya untuk mengoptimalkan pendekatan; Saya bisa membaca file baris demi baris, hanya mendekode ketika saya menemukan baris yang berisi persis "}":
Jika Anda kebetulan bekerja dengan JSON kompak satu per baris yang lolos dari baris baru dalam literal string, Anda dapat lebih menyederhanakan pendekatan ini dengan aman:
Jelas, pendekatan sederhana ini hanya bekerja untuk jenis JSON yang sangat spesifik. Namun, jika asumsi ini berlaku, solusi ini berfungsi dengan benar dan cepat.
sumber
Jika Anda menggunakan instance json.JSONDecoder, Anda dapat menggunakan
raw_decode
fungsi anggota. Ini mengembalikan tupel representasi python dari nilai JSON dan indeks ke tempat penguraian berhenti. Ini membuatnya mudah untuk memotong (atau mencari di objek aliran) nilai JSON yang tersisa. Saya tidak begitu senang dengan loop sementara ekstra untuk melewati ruang putih antara nilai JSON yang berbeda dalam masukan tetapi itu menyelesaikan pekerjaan menurut pendapat saya.Versi selanjutnya jauh lebih pendek dan memakan bagian dari string yang sudah diurai. Tampaknya untuk beberapa alasan panggilan kedua json.JSONDecoder.raw_decode () tampaknya gagal ketika karakter pertama dalam string adalah spasi, itu juga alasan mengapa saya melewatkan spasi di whileloop di atas ...
Dalam dokumentasi tentang kelas json.JSONDecoder, metode raw_decode https://docs.python.org/3/library/json.html#encoders-and-decoders berisi yang berikut ini:
Dan data asing ini dapat dengan mudah menjadi nilai JSON lainnya. Dengan kata lain, metode tersebut mungkin ditulis dengan tujuan ini dalam pikiran.
Dengan input.txt menggunakan fungsi atas saya mendapatkan contoh keluaran seperti yang disajikan dalam pertanyaan asli.
sumber
Anda dapat menggunakan https://pypi.org/project/json-stream-parser/ untuk tujuan itu.
keluaran
sumber