Come estrarre i dati da un file JSON


13

Ho bin cercando una soluzione per la mia domanda ma non ho trovato o meglio detto che non l'ho trovato con quello che ho trovato. Quindi parliamo di quale sia il mio problema. Sto usando un software di controllo Smart Home su un Raspberry Pi e, come ho scoperto questo fine settimana usando la funzione di ricezione pilight, posso catturare i dati dal mio sensore di temperatura esterno. L'output di pilight-reception è simile al seguente:

{
        "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
}

Ora la mia domanda per te: come diamine posso estrarre la temperatura e l'umidità da dove l'id è 1490. E come mi consiglieresti di fare questo controllo frequentemente? Con un cron job che viene eseguito ogni 10 minuti, crea un output del pilight-reception, estrae i dati dell'output e li invia all'API di Smart Home Control.

Qualcuno che ha un'idea - grazie mille


3
Il formato sembra essere JSON . Esistono molti modi per analizzare JSON. Dipende da cosa ti senti a tuo agio. Pitone? JavaScript? Qualcos'altro?
muru,

Conosco un po 'di Python e un po' di JavaScript, soprattutto conosco C ++ e C #. Ma dopo aver visto tutti i comandi awk e sed, ho pensato che fosse un comando semplice xD
Raul Garcia Sanchez,

1
Non è difficile con awke sedpurché l'output JSON mantenga la formattazione mostrata qui, cosa che non serve - gli spazi bianchi non contano per JSON. Ad esempio, questo awkcomando: awk '/temperature|humidity/ {print $2}'è vicino.
muru,

4
con ksh93json parsing è integrato in read.
Mikeserv,

1
controlla wheezy-backport. potrebbe essere lì, risparmiando un aggiornamento a jessie (a meno che tu non stia pianificando di aggiornare comunque). aha! è backportato in wheezy. Packages.debian.org/wheezy-backports/jq
cas

Risposte:


22

È possibile utilizzare jqper elaborare i file JSON nella shell.

Ad esempio, ho salvato il tuo file json di esempio come raul.jsone poi ho eseguito:

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

jq è disponibile preconfezionato per la maggior parte delle distribuzioni Linux.

C'è probabilmente un modo per farlo da jqsolo, ma il modo più semplice che ho trovato per ottenere entrambi i valori desiderati su una riga è usare xargs. Per esempio:

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

oppure, se desideri scorrere ogni .message.idistanza, possiamo aggiungere .message.idall'output e utilizzare xargs -n 3poiché sappiamo che ci saranno tre campi (id, temperatura, umidità):

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

È quindi possibile post-elaborare quell'output con awk o altro.


Infine, sia Python che Perl hanno librerie eccellenti per l'analisi e la manipolazione dei dati JSON. Come molte altre lingue, tra cui php e java.


2
in particolare,jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
glenn jackman,

1
o, in bash,{ read temp; read hum; } < <(jq ...)
glenn jackman,

1
Vedi la mia risposta che usa semplicemente grep. Potrebbe non funzionare per alcune versioni specifiche di grep, ma è più semplice che jqin questo scenario, anche se jqè progettato specificamente per l'analisi di JSON. Ho dato jqun voto alla risposta, comunque. È davvero uno strumento per il lavoro, ma a volte puoi semplicemente rimuovere le graffette con le dita piuttosto che cercare un dispositivo di rimozione graffette.
rubynorails

2
json non può essere analizzato in modo affidabile con espressioni regolari non più di quanto possano fare xml o html. e la maggior parte dei dati json (ad esempio recuperati tramite un'API Web) non viene formattata in modo corretto con feed di riga e rientri aggiuntivi. per analizzare json in modo affidabile, è necessario un parser json. jqè uno di questi per gli script di shell. altre lingue hanno librerie di analisi json.
Cas

1
qualsiasi cosa può essere analizzata in modo affidabile con espressioni regolari. dipende solo da quanto molti si usa. come pensi che jqfaccia?
mikeserv,

0

jqè di gran lunga la soluzione più elegante. Con awkte potresti scrivere

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

0

Per coloro che non capiscono l'avanzato awkcome vorrebbero (come persone come me) e non hanno jqpreinstallato, una soluzione semplice sarebbe collegare un paio di comandi nativi insieme in questo modo:

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

Se stai solo cercando di ottenere i valori, è più semplice usare greppiuttosto che awko sed:

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

Per fornire una spiegazione, questo mi sembra il modo più semplice.

  • La grep -A2afferra la linea che si sta cercando in JSON con i seguenti 2 linee, che contengono la temperatura e l'umidità.
  • Il tubo per grep -ostampare semplicemente solo cifre numeriche separate da un .(che non si verificherà mai sulla prima 1490riga, quindi ti rimangono i tuoi 2 valori: temperatura e umidità. Molto semplice. Anche più semplice dell'uso jq, secondo me.

0

Il mio strumento preferito per l'elaborazione di JSON sulla riga di comando è jq. Tuttavia, se non hai installato jq puoi fare abbastanza bene con 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

0

l'output è un set di snippet JSON anziché un JSON completo. Se / una volta riorganizzato l'output in modo che sia un JSON integrale, ad esempio in questo modo (supponendo che l'output sia in file.json):

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

quindi è facile ottenere quello che vuoi con lo jtcstrumento (disponibile su: 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 $ 

nell'esempio precedente rilasciare -lse non si desidera etichette stampate

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.