Как извлечь данные из файла JSON

13

У меня есть бин, ищущий решение для моего вопроса, но не нашел или лучше сказал, что я не получил это с тем, что я нашел. Итак, давайте поговорим о том, о чем моя проблема. Я использую программное обеспечение Smart Home Control на Raspberry Pi, и, как я узнал в эти выходные, используя систему приема-приема, я могу получить данные с моего датчика температуры наружного воздуха. Вывод 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
}

Теперь мой вопрос к вам: Как, черт возьми, я могу извлечь температуру и влажность, где идентификатор 1490. И как бы вы посоветовали мне делать это часто? При выполнении задания cron, которое выполняется каждые 10 минут, создается вывод pilight-receive, извлекаются данные этого вывода и отправляется в Api Smart Home Control.

У кого-то есть идея - большое спасибо

Рауль Гарсия Санчес
источник
3
Формат, кажется, JSON . Есть много способов разобрать JSON. Это зависит от того, что вам удобно. Python? JavaScript? Что-то другое?
Муру
Я немного знаю Python и немного JavaScript, в основном я знаю C ++ и C #. Но, посмотрев все команды awk и sed, я, должно быть, немного простую команду xD
Рауль Гарсия Санчес
1
Это не сложно, awkи sedпри условии, что вывод JSON сохраняет форматирование, показанное здесь, в этом нет необходимости - пробел не имеет значения для JSON. Например, эта awkкоманда: awk '/temperature|humidity/ {print $2}'близка.
Муру
4
с помощью ksh93JSON разбор встроен в read.
mikeserv
1
проверьте wheezy-backports. это может быть там, сохраняя вам обновление до Джесси (если вы не планировали обновить в любом случае). Ага! это перенесено на хрипить. packages.debian.org/wheezy-backports/jq
около

Ответы:

22

Вы можете использовать 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сам по себе, но самый простой способ получить оба требуемых значения в одной строке - использовать xargs. Например:

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

или, если вы хотите пройтись по каждому .message.idэкземпляру, мы можем добавить .message.idк выводу и использовать, xargs -n 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.

саз
источник
2
в частности,jq 'select(.message.id == 1490) | .message.temperature, .message.humidity' raul.json
Гленн Джекман
1
или, в Баш,{ read temp; read hum; } < <(jq ...)
Гленн Джекман
1
Смотрите мой ответ, который просто использует grep. Это может не работать для некоторых конкретных версий grep, но это более прямолинейно, чем jqв этом сценарии, даже если jqоно разработано специально для анализа JSON. Я дал jqответ, хотя, несмотря ни на что. Это действительно инструмент для работы, но иногда вы можете просто удалить скобы пальцами, а не искать устройство для удаления скрепок.
Rubynorails
2
json не может быть надежно проанализирован с помощью регулярных выражений так же, как xml или html. и большинство данных JSON (например, извлеченных через веб-API) не отформатированы с дополнительными переводами строк и отступами. Для надежного анализа JSON необходим анализатор JSON. jqодин такой для сценариев оболочки. другие языки имеют библиотеки синтаксического анализа json.
cas
1
все может быть надежно проанализировано с помощью регулярных выражений. это зависит только от того, сколько вы используете. как ты думаешь jqэто делает?
mikeserv
0

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

Для тех, кто не понимает продвинутый уровень 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мой взгляд, даже проще, чем использовать .
rubynorails
источник
0

Мой инструмент выбора для обработки 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
NWK
источник
0

ваш вывод представляет собой набор фрагментов 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если вы не хотите печатать этикетки

Дмитрий Львович
источник