web-dev-qa-db-ja.com

JSONファイルからデータを抽出する方法

私は自分の質問の解決策をビンで検索していますが、見つからなかった、または見つけたものでそれを取得できなかったと述べた。だから私の問題が何であるかについて話しましょう。私はRaspberry Piでスマートホームコントロールソフトウェアを使用しています。今週末、pilight-receiveを使用していることを知ったので、屋外の温度センサーからデータを取得できます。 pilight-receiveの出力は次のようになります。

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

ここで私の質問です。IDが1490の場所から温度と湿度を抽出するにはどうすればいいですか。これを頻繁に確認する方法を教えてください。 10分ごとに実行されるcronジョブによって、pilight-receiveの出力を作成し、出力のデータを抽出して、スマートホームコントロールAPIにプッシュします。

誰かがアイデアを持っている-どうもありがとう

13

jqを使用して、シェルでjsonファイルを処理できます。

たとえば、サンプルのjsonファイルをraul.jsonとして保存して実行しました。

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

jq は、ほとんどのLinuxディストリビューションで事前にパッケージ化されています。

おそらくjq自体でそれを行う方法がありますが、1つの行で両方の必要な値を取得するために見つけた最も簡単な方法は、xargsを使用することです。例えば:

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

または、各.message.idインスタンスをループ処理する場合は、出力に.message.idを追加してxargs -n 3を使用できます。これは、3つのフィールド(id、温度、湿度)があることがわかっているためです。 :

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

次に、その出力をawkなどで後処理できます。


最後に、pythonとPerlの両方に、jsonデータを解析および操作するための優れたライブラリがあります。phpやJavaを含む他のいくつかの言語と同様に。

23
cas

コマンドラインでJSONを処理するために選択したツールはjqです。ただし、jqがインストールされていない場合は、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

高度なawkを理解しておらず、知りたいと思っている人(私のような人など)がjqをプリインストールしていない場合、簡単な解決策はパイピングです。次のようにいくつかのネイティブコマンドを一緒に:

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

値を取得するだけの場合は、grepまたはawkよりもsedを使用する方が簡単です:

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

説明するために、これは私にとって最も簡単な方法のようです。

  • grep -A2は、JSONで探している行と、温度と湿度を含む次の2行を取得します。
  • grep -oへのパイプは、.で区切られた数値のみを出力するだけです(最初の1490行では発生しないため、温度と湿度の2つの値が残ります。非常に単純です。私の意見では、jqを使用するよりもさらに単純です。
0
rubynorails

出力は、完全なJSONではなく、一連のJSONスニペットです。 /の場合、出力を統合JSONに再配置します。このように(出力がfile.json):

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

次に、jtcツールを使用して簡単に目的を達成できます( 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 $ 

上記の例では、ドロップ-lラベルを印刷したくない場合

0
Dmitry L.

jqは、これまでで最もエレガントなソリューションです。 awkを使用すると、次のように書くことができます

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