So extrahieren Sie Daten aus einer JSON-Datei

13

Ich habe nach einer Lösung für meine Frage gesucht, aber keine gefunden oder besser gesagt, ich habe sie nicht mit dem bekommen, was ich gefunden habe. Sprechen wir also darüber, worum es bei meinem Problem geht. Ich verwende eine Smart Home Control Software auf einem Raspberry Pi und wie ich an diesem Wochenende mit pilight-receive herausgefunden habe, kann ich die Daten von meinem Außentemperatursensor abrufen. Die Ausgabe von pilight-receive sieht folgendermaßen aus:

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

Nun meine Frage an Sie: Wie zum Teufel kann ich die Temperatur und Luftfeuchtigkeit von der ID 1490 extrahieren? Und wie würden Sie mir empfehlen, dies regelmäßig zu überprüfen? Durch einen Cron-Job, der alle 10 Minuten ausgeführt wird, eine Ausgabe des Pilight-Empfangs erstellt, die Daten der Ausgabe extrahiert und an die Smart Home Control-API übermittelt.

Jemand, der eine Idee hat - vielen Dank

Raul Garcia Sanchez
quelle
3
Das Format scheint JSON zu sein . Es gibt viele Möglichkeiten, JSON zu analysieren. Es kommt darauf an, womit Sie sich wohl fühlen. Python? JavaScript? Etwas anderes?
Muru
Ich kenne ein bisschen Python und ein bisschen JavaScript, meistens kenne ich C ++ und C #. Aber nachdem ich alle awk- und sed-Befehle gesehen habe, dachte ich, dass dies ein einfacher Befehl sein muss xD
Raul Garcia Sanchez
1
Es ist nicht schwierig, awkund sedvorausgesetzt, die JSON-Ausgabe behält die hier gezeigte Formatierung bei, die sie nicht benötigt - Leerzeichen spielen für JSON keine Rolle. Zum Beispiel ist dieser awkBefehl: awk '/temperature|humidity/ {print $2}'close.
Muru
4
mit ksh93json ist das parsing dazu eingebaut read.
mikeserv
1
wheezy-backports überprüfen Es könnte sich dort befinden und Ihnen ein Upgrade auf Jessie ersparen (es sei denn, Sie wollten sowieso ein Upgrade durchführen). Aha! Es ist zu keuchen zurückportiert. packages.debian.org/wheezy-backports/jq
cas

Antworten:

22

Mit können Sie jqJSON-Dateien in der Shell verarbeiten.

Ich habe beispielsweise Ihre Beispiel-JSON-Datei als gespeichert raul.jsonund dann ausgeführt:

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

jq ist für die meisten Linux-Distributionen vorinstalliert.

Es gibt wahrscheinlich eine Möglichkeit, dies jqselbst zu tun , aber die einfachste Möglichkeit, die beiden gewünschten Werte in einer Zeile zu erhalten, ist die Verwendung xargs. Beispielsweise:

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

oder, wenn Sie jede .message.idInstanz durchlaufen möchten , können wir .message.iddie Ausgabe erweitern und verwenden, xargs -n 3da wir wissen, dass es drei Felder gibt (ID, Temperatur, Luftfeuchtigkeit):

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

Sie können diese Ausgabe dann mit awk oder was auch immer nachbearbeiten.


Schließlich verfügen sowohl Python als auch Perl über hervorragende Bibliotheken zum Parsen und Bearbeiten von JSON-Daten. Wie einige andere Sprachen, einschließlich PHP und Java.

cas
quelle
2
Insbesonderejq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
Jackman glenn
1
oder, in Bash,{ read temp; read hum; } < <(jq ...)
Glenn Jackman
1
Siehe meine Antwort, die einfach verwendet grep. Es funktioniert möglicherweise nicht für bestimmte Versionen von grep, ist aber einfacher als jqin diesem Szenario, obwohl jqes speziell für das Parsen von JSON entwickelt wurde. jqTrotzdem habe ich die Antwort positiv bewertet. Es ist in der Tat ein Werkzeug für den Job, aber manchmal können Sie Heftklammern einfach mit Ihren Fingern entfernen, anstatt nach einem Klammerentferner zu suchen.
Rubynorails
2
json kann nicht mehr zuverlässig mit regulären Ausdrücken analysiert werden als xml oder html. und die meisten JSON-Daten (z. B. über eine Web-API abgerufen) sind mit zusätzlichen Zeilenumbrüchen und Einrückungen nicht gut formatiert. Um JSON zuverlässig zu analysieren, benötigen Sie einen JSON-Parser. jqist eine solche für Shell-Skripte. Andere Sprachen haben JSON-Parsing-Bibliotheken.
cas
1
Alles kann zuverlässig mit regulären Ausdrücken analysiert werden. Es hängt nur davon ab, wie viele Sie verwenden. wie denkst du jqtut es
mikeserv
0

jqist mit Abstand die eleganteste Lösung. Mit awkkönntest du schreiben

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

Für diejenigen, die Advanced nicht awkso gut verstehen, wie sie es jqmöchten (wie Leute wie ich) und die es nicht vorinstalliert haben, wäre eine einfache Lösung, ein paar native Befehle wie folgt zusammenzufügen:

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

Wenn Sie nur versuchen, die Werte zu ermitteln, ist es einfacher, grepstatt awkoder nur Folgendes zu verwenden sed:

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

Um eine Erklärung zu geben, scheint mir dies der einfachste Weg zu sein.

  • Das grep -A2 greift nach der Zeile, die Sie im JSON suchen, zusammen mit den folgenden 2 Zeilen, die die Temperatur und Luftfeuchtigkeit enthalten.
  • Die Pipe gibt grep -oeinfach nur numerische Ziffern aus, die durch ein .(das wird in der ersten 1490Zeile nie vorkommen) getrennt sind, sodass Sie Ihre 2 Werte - Temperatur und Luftfeuchtigkeit - behalten. Sehr einfach. Noch einfacher als die Verwendung jq, meiner Meinung nach.
Rubinrillen
quelle
0

Mein bevorzugtes Werkzeug für die Verarbeitung von JSON in der Befehlszeile ist jq. Wenn Sie jedoch jq nicht installiert haben, können Sie Perl recht gut einsetzen:

# 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
quelle
0

Ihre Ausgabe besteht aus einer Reihe von JSON-Snippets und nicht aus einer vollständigen JSON. Wenn / sobald Sie Ihre Ausgabe zu einem integralen JSON umgestalten, z. B. so (vorausgesetzt, Ihre Ausgabe ist in file.json):

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

Dann ist es einfach, mit dem jtcTool das zu erreichen, was Sie wollen (verfügbar unter: 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 $ 

Im obigen Beispiel löschen -lSie, wenn Sie keine gedruckten Etiketten möchten

Dmitry L.
quelle