Como extrair dados de um arquivo JSON


13

Eu tenho bin procurando uma solução para a minha pergunta, mas não encontrei uma ou melhor, disse que não a obtive com o que encontrei. Então, vamos falar sobre o que é o meu problema. Estou usando um software de controle doméstico inteligente em um Raspberry Pi e, como descobri neste final de semana usando o pilight-receive, posso pegar os dados do meu sensor de temperatura externo. A saída do pilight-receive é assim:

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

Agora, minha pergunta é: como diabos posso extrair a temperatura e a umidade de onde o id é 1490. E como você recomendaria que eu fizesse isso com freqüência? Por um trabalho cron que é executado a cada 10 minutos, cria uma saída do pilight-receive, extrai os dados da saída e os envia para a API do Smart Home Control.

Alguém tendo uma ideia - muito obrigado


3
O formato parece ser JSON . Existem várias maneiras de analisar o JSON. Depende do que você está acostumado. Pitão? JavaScript? Algo mais?
— Muru

Eu sei um pouco de Python e um pouco de JavaScript, principalmente eu sei C ++ e C #. Mas depois de ver todo o awk e comandos sed I embora o deve haver algum xD comando fácil
— Raul Garcia Sanchez

1
Não é difícil awke seddesde que a saída JSON retenha a formatação mostrada aqui, o que não é necessário - espaço em branco não importa para JSON. Por exemplo, este awkcomando: awk '/temperature|humidity/ {print $2}'está próximo.
— Muru

4
com ksh93json parsing está embutido em read.
— mikeserv

1
verifique wheezy-backports. pode estar lá, salvando uma atualização para jessie (a menos que você planeje fazer a atualização de qualquer maneira). aha! É suportado para chiado. packages.debian.org/wheezy-backports/jq
— cas

Respostas:


22

Você pode usar jqpara processar arquivos json no shell.

Por exemplo, salvei seu arquivo json de exemplo raul.jsone executei:

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

O jq está disponível pré-empacotado para a maioria das distribuições linux.

Provavelmente existe uma maneira de fazer isso por jqsi só, mas a maneira mais simples que encontrei para obter os dois valores desejados em uma linha é usar xargs. Por exemplo:

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

ou, se você desejar percorrer cada .message.idinstância, podemos adicionar .message.idà saída e usar xargs -n 3como sabemos que haverá três campos (id, temperatura, umidade):

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

Você pode pós-processar essa saída com awk ou o que quer que seja.


Por fim, python e perl têm excelentes bibliotecas para analisar e manipular dados json. Assim como várias outras línguas, incluindo php e java.


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

1
ou, em bash,{ read temp; read hum; } < <(jq ...)
— glenn jackman

1
Veja minha resposta que simplesmente usa grep. Pode não funcionar para algumas versões específicas do grep, mas é mais direto do que jqneste cenário, embora tenha jqsido projetado especificamente para analisar o JSON. Dei jquma resposta positiva, apesar de tudo. É realmente uma ferramenta para o trabalho, mas às vezes você pode simplesmente remover grampos com os dedos, em vez de procurar um removedor de grampos.
— amigos estão dizendo sobre rubinorails

2
O json não pode ser analisado de maneira confiável com expressões regulares, assim como o xml ou o html. e a maioria dos dados json (por exemplo, buscados através de uma API da Web) não são formatados de maneira agradável com feeds de linha e recuos extras. para analisar json de maneira confiável, você precisa de um analisador de json. jqé um desses para scripts de shell. outros idiomas têm bibliotecas de análise de json.
— cas

1
qualquer coisa pode ser analisada de maneira confiável com expressões regulares. só depende de como muitos que você usa. como você acha jqisso?
— mikeserv

0

jqé de longe a solução mais elegante. Com awkvocê poderia escrever

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

0

Para aqueles que não entendem avançado awktão bem quanto gostariam (como pessoas como eu) e não têm jqpré-instalado, uma solução fácil seria reunir alguns comandos nativos da seguinte forma:

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

Se você está apenas tentando obter os valores, é mais fácil usar em grepvez de awkou sed:

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

Para fornecer uma explicação, esta parece ser a maneira mais simples para mim.

  • Ele grep -A2pega a linha que você está procurando no JSON, juntamente com as 2 linhas a seguir, que contêm a temperatura e a umidade.
  • O tubo para grep -osimplesmente imprimir apenas dígitos numéricos separados por um .(que nunca ocorrerá na primeira 1490linha, então você ficará com seus 2 valores - temperatura e umidade. Muito simples. Ainda mais simples do que usar jq, na minha opinião.

0

Minha ferramenta de escolha para processar JSON na linha de comandos é jq. No entanto, se você não possui o jq instalado, pode se sair muito bem com o 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

sua saída é um conjunto de snippets JSON em vez de um JSON completo. Se / uma vez que você reorganizar sua saída para ser um JSON integral, por exemplo, assim (assumindo que sua saída está dentro file.json):

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

é fácil conseguir o que deseja com a jtcferramenta (disponível em: 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 $ 

no exemplo acima, solte -lse você não quiser etiquetas impressas

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.