Cara mengekstrak data dari file JSON

13

Saya memiliki bin mencari solusi untuk pertanyaan saya tetapi tidak menemukan atau lebih baik mengatakan saya tidak mendapatkannya dengan apa yang saya temukan. Jadi mari kita bicara tentang masalah saya. Saya menggunakan Smart Home Control Software pada Raspberry Pi dan ketika saya mengetahui akhir pekan ini menggunakan pilight-accept, saya dapat menangkap data dari sensor suhu luar ruang saya. Output Pilight-accept terlihat seperti itu:

{
        "message": {
                "id": 4095,
                "temperature": 409.5
        },
        "origin": "receiver",
        "protocol": "alecto_wsd17",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 3
}
{
        "message": {
                "id": 1490,
                "temperature": 25.1,
                "humidity": 40.0,
                "battery": 1
        },
        "origin": "receiver",
        "protocol": "alecto_ws1700",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 3
}
{
        "message": {
                "id": 2039,
                "temperature": 409.5
        },
        "origin": "receiver",
        "protocol": "alecto_wsd17",
        "uuid": "0000-b8-27-eb-0f3db7",
        "repeats": 4
}

Sekarang pertanyaan saya kepada Anda: Bagaimana sih saya bisa mengekstraksi suhu dan kelembaban dari tempat id adalah 1490. Dan bagaimana Anda merekomendasikan saya untuk melakukan ini? Dengan pekerjaan cron yang berjalan setiap 10 menit, menciptakan output dari Pilight-accept, mengekstrak data output dan mendorongnya ke Smart Home Control Api.

Seseorang yang punya ide - terima kasih banyak

Raul Garcia Sanchez
sumber
3
Formatnya sepertinya JSON . Ada banyak cara untuk mengurai JSON. Itu tergantung pada apa yang membuat Anda nyaman. Python? JavaScript? Sesuatu yang lain
muru
Saya tahu sedikit Python dan sedikit JavaScript kebanyakan saya tahu C ++ dan C #. Tetapi setelah melihat semua perintah awk dan sed, saya pikir itu pasti perintah mudah xD
Raul Garcia Sanchez
1
Ini tidak sulit dengan awkdan sedasalkan output JSON mempertahankan format yang ditunjukkan di sini, yang tidak perlu - spasi putih tidak masalah untuk JSON. Sebagai contoh, awkperintah ini : awk '/temperature|humidity/ {print $2}'sudah dekat.
muru
4
dengan ksh93parsing json dibangun untuk read.
mikeserv
1
periksa wheezy-backports. mungkin ada di sana, menghemat Anda upgrade ke jessie (kecuali Anda memang berencana untuk meng-upgrade). aha! itu didukung untuk mengi. packages.debian.org/wheezy-backports/jq
cas

Jawaban:

22

Anda dapat menggunakan jquntuk memproses file json di shell.

Misalnya, saya menyimpan file json sampel Anda sebagai raul.jsondan kemudian berlari:

$ jq .message.temperature raul.json 
409.5
25.1
409.5
$ jq .message.humidity raul.json 
null
40
null

jq tersedia pra-paket untuk sebagian besar distro linux.

Mungkin ada cara untuk melakukannya jqsendiri, tetapi cara paling sederhana yang saya temukan untuk mendapatkan kedua nilai yang diinginkan pada satu baris adalah dengan menggunakannya xargs. Sebagai contoh:

$ jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json | xargs
25.1 40

atau, jika Anda ingin mengulang setiap .message.idcontoh, kita dapat menambahkan .message.idke output dan menggunakan xargs -n 3seperti yang kita tahu bahwa akan ada tiga bidang (id, suhu, kelembaban):

jq '.message.id, .message.temperature, .message.humidity' raul.json | xargs -n 3
4095 409.5 null
1490 25.1 40
2039 409.5 null

Anda kemudian dapat memposting proses itu dengan awk atau apa pun.


Akhirnya, baik python dan perl memiliki pustaka yang sangat baik untuk mem-parsing dan memanipulasi data json. Seperti halnya beberapa bahasa lain, termasuk php dan java.

cas
sumber
2
secara khusus,jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
glenn jackman
1
atau, dalam bash,{ read temp; read hum; } < <(jq ...)
glenn jackman
1
Lihat jawaban saya yang hanya menggunakan grep. Ini mungkin tidak bekerja untuk beberapa versi tertentu grep, tetapi lebih mudah daripada jqdalam skenario ini, meskipun jqdirancang khusus untuk mem-parsing JSON. Saya memang memberikan jqjawaban yang mendukung, bagaimanapun. Ini memang alat untuk pekerjaan itu, tetapi kadang-kadang Anda bisa dengan mudah melepas staples dengan jari Anda daripada mencari-cari penghapus staples.
rubynorails
2
json tidak dapat diurai dengan benar dengan ekspresi reguler seperti halnya xml atau html. dan sebagian besar data json (mis. diambil melalui api web) tidak diformat dengan baik dengan umpan baris tambahan dan indentasi. untuk mengurai json andal, Anda membutuhkan pengurai json. jqadalah salah satunya untuk skrip shell. bahasa lain memiliki parsing perpustakaan json.
cas
1
apa pun dapat diurai secara andal dengan ekspresi reguler. itu tergantung pada berapa banyak yang Anda gunakan. bagaimana menurut Anda jqmelakukannya?
mikeserv
0

jqsejauh ini merupakan solusi yang paling elegan. Dengan awkAnda bisa menulis

awk -v id=1490 '
    $1 == "\"id\":" && $2 == id"," {matched = 1}
    $1 == "}," {matched = 0}
    matched && $1 ~ /temperature|humidity/ {sub(/,/,"", $2); print $2}
' file
glenn jackman
sumber
0

Bagi mereka yang tidak mengerti tingkat lanjut awkseperti yang mereka inginkan (seperti orang-orang seperti saya) dan tidak memiliki jqpra-instal, solusi mudah akan menyalurkan beberapa perintah asli bersama-sama seperti:

grep -A2 '"id": 1490,' stats.json | sed '/1490/d;s/"//g;s/,//;s/\s*//'

Jika Anda hanya mencoba untuk mendapatkan nilai, lebih mudah hanya menggunakan grepdaripada awkatau sed:

grep -A2 '"id": 1490,' stats.json | grep -o "[0-9]*\.[0-9]*"

Untuk memberikan penjelasan, ini sepertinya cara paling sederhana bagi saya.

  • The grep -A2meraih garis yang Anda cari dalam JSON bersama dengan 2 baris berikut, yang berisi suhu dan kelembaban.
  • Pipa untuk grep -ohanya mencetak digit angka yang dipisahkan oleh .(yang tidak akan pernah terjadi pada 1490baris pertama , sehingga Anda dibiarkan dengan 2 nilai Anda - suhu dan kelembaban. Sangat sederhana. Bahkan lebih sederhana daripada menggunakan jq, menurut saya.
rubynorails
sumber
0

Alat pilihan saya untuk memproses JSON pada baris perintah adalah jq. Namun, jika Anda tidak menginstal jq, Anda dapat melakukannya dengan cukup baik dengan Perl:

# perl -MJSON -e '$/ = undef; my $data = <>; for my $hash (new JSON->incr_parse($data)) { my $msg = $hash->{message}; print "$msg->{temperature} $msg->{humidity}\n" if $msg->{id} == 1490 }' < data.json
25.1 40
nwk
sumber
0

output Anda adalah satu set cuplikan JSON daripada JSON lengkap. Jika / setelah Anda mengatur ulang output Anda menjadi JSON yang tidak terpisahkan, misal seperti ini (dengan asumsi output Anda ada dalam file.json):

echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]"

maka mudah untuk mencapai apa yang Anda inginkan dengan jtcalat (tersedia di: https://github.com/ldn-softdev/jtc ):

bash $ echo "[ $(cat file.json | sed -E 's/^}$/},/; $d') }]" | jtc -x "[id]:<1490>d [-1]" -y[temperature] -y[humidity] -l
"temperature": 25.1
"humidity": 40.0
bash $ 

pada contoh drop di atas -ljika Anda tidak ingin label yang dicetak

Dmitry L.
sumber