web-dev-qa-db-fra.com

Comment extraire des données d'un fichier JSON

J'ai bin chercher une solution à ma question, mais je n'ai pas trouvé de réponse ou mieux dit que je ne l'ai pas trouvée avec ce que j'ai trouvé. Permet donc de parler de mon problème. J'utilise un logiciel de contrôle de maison intelligente sur un Raspberry Pi et, comme je l'ai découvert ce week-end en utilisant la réception pilight, je peux capturer les données de mon capteur de température extérieure. La sortie de pilight-receive ressemble à ceci:

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

Maintenant, ma question est la suivante: comment diable puis-je extraire la température et l'humidité de l'endroit où se trouve l'ID 1490. Et comment me recommanderiez-vous de vérifier cela fréquemment? Par un travail cron qui s'exécute toutes les 10 minutes, crée une sortie de la réception pilight, extrait les données de la sortie et les pousse vers l'API Smart Home Control.

Quelqu'un a une idée - merci beaucoup

13

Vous pouvez utiliser jq pour traiter les fichiers json dans Shell.

Par exemple, j'ai enregistré votre exemple de fichier json en tant que raul.json puis a couru:

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

jq est disponible pré-emballé pour la plupart des distributions Linux.

Il y a probablement un moyen de le faire dans jq lui-même, mais le moyen le plus simple que j'ai trouvé pour obtenir les deux valeurs souhaitées sur une seule ligne est d'utiliser xargs. Par exemple:

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

ou, si vous souhaitez parcourir chaque .message.id instance, nous pouvons ajouter .message.id à la sortie et utilisez xargs -n 3 car nous savons qu'il y aura trois champs (id, température, humidité):

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

Vous pouvez ensuite post-traiter cette sortie avec awk ou autre chose.


Enfin, python et Perl ont d'excellentes bibliothèques pour analyser et manipuler les données json. Comme le font plusieurs autres langages, y compris php et Java.

23
cas

Mon outil de choix pour le traitement de JSON sur la ligne de commande est jq. Cependant, si jq n'est pas installé, vous pouvez très bien faire avec 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
nwk

Pour ceux qui ne comprennent pas le awk avancé comme ils le voudraient (comme les gens comme moi) et qui n'ont pas jq pré-installé, une solution simple serait la tuyauterie quelques commandes natives ensemble comme ceci:

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

Si vous essayez seulement d'obtenir les valeurs, il est plus facile d'utiliser simplement grep plutôt que awk ou sed:

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

Pour fournir une explication, cela me semble être le moyen le plus simple.

  • Le grep -A2 saisit la ligne que vous recherchez dans le JSON avec les 2 lignes suivantes, qui contiennent la température et l'humidité.
  • Le tuyau vers grep -o imprime simplement uniquement les chiffres numériques séparés par un . (qui ne se produira jamais le premier 1490 ligne, vous êtes donc laissé avec vos 2 valeurs - température et humidité. Très simple. Encore plus simple que d'utiliser jq, à mon avis.
0
rubynorails

votre sortie est un ensemble d'extraits JSON plutôt qu'un JSON complet. Si/une fois que vous réorganisez votre sortie pour qu'elle soit un JSON intégral, par ex. comme ceci (en supposant que votre sortie est dans file.json):

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

alors il est facile de réaliser ce que vous voulez avec l'outil jtc (disponible sur: 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 $ 

dans l'exemple ci-dessus drop -l si vous ne voulez pas d'étiquettes imprimées

0
Dmitry L.

jq est de loin la solution la plus élégante. Avec awk vous pourriez écrire

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