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.