Analisa o JSON usando o Python?

15

Eu tenho um arquivo JSON members.json como abaixo.

{
   "took": 670,
   "timed_out": false,
   "_shards": {
      "total": 8,
      "successful": 8,
      "failed": 0
   },
   "hits": {
      "total": 74,
      "max_score": 1,
      "hits": [
         {
            "_index": "2000_270_0",
            "_type": "Medical",
            "_id": "02:17447847049147026174478:174159",
            "_score": 1,
            "_source": {
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcf",
               "memberFirstName": "Uri",
               "memberMiddleName": "Prayag",
               "memberLastName": "Dubofsky"
            }
         }, 
         {
            "_index": "2000_270_0",
            "_type": "Medical",
            "_id": "02:17447847049147026174478:174159",
            "_score": 1,
            "_source": {
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcG",
               "memberFirstName": "Uri",
               "memberMiddleName": "Prayag",
               "memberLastName": "Dubofsky"
            }
         }
      ]
   }
}

Eu quero analisá-lo usando o script bash para obter apenas a lista do campo memberId .

A saída esperada é:

memberIds
----------- 
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG

Eu tentei adicionar o seguinte código bash + python a .bashrc :

function getJsonVal() {
   if [ \( $# -ne 1 \) -o \( -t 0 \) ]; then
       echo "Usage: getJsonVal 'key' < /tmp/file";
       echo "   -- or -- ";
       echo " cat /tmp/input | getJsonVal 'key'";
       return;
   fi;
   cat | python -c 'import json,sys;obj=json.load(sys.stdin);print obj["'$1'"]';
}

E depois chamado:

$ cat members.json | getJsonVal "memberId"

Mas joga:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
KeyError: 'memberId'

Referência

link

    
por prayagupd 21.02.2014 / 07:28

8 respostas

21

Se você usasse:

 $ cat members.json | \
     python -c 'import json,sys;obj=json.load(sys.stdin);print obj;'

você pode inspecionar a estrutura do dicionário aninhado obj e ver que sua linha original deve ler:

$ cat members.json | \
    python -c 'import json,sys;obj=json.load(sys.stdin);print obj["hits"]["hits"][0]["_source"]["'$1'"]';

para o elemento "memberId". Desta forma, você pode manter o Python como um oneliner.

Se houver vários elementos no elemento "hits" aninhados, você poderá fazer algo como:

$ cat members.json | \
python -c '
import json, sys
obj=json.load(sys.stdin)
for y in [x["_source"]["'$1'"] for x in obj["hits"]["hits"]]:
    print y
'

A solução de Chris Down é melhor para encontrar um único valor para chaves (exclusivas) em qualquer nível.

Com o meu segundo exemplo que imprime vários valores, você está atingindo os limites do que deve tentar com um liner, nesse ponto, vejo pouca razão para fazer metade do processamento no bash e mover para um solução completa em Python.

    
por 21.02.2014 / 08:21
7

Outra maneira de fazer isso no bash é usar jshon . Aqui está uma solução para o seu problema usando jshon :

$ jshon -e hits -e hits -a -e _source -e memberId -u < foo.json
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG

As opções -e extraem valores do json. O -a itera a matriz e o -u decodifica a sequência final.

    
por 21.02.2014 / 07:58
5

Bem, sua chave claramente não está na raiz do objeto. Tente algo assim:

json_key() {
    python -c '
import json
import sys

data = json.load(sys.stdin)

for key in sys.argv[1:]:
    try:
        data = data[key]
    except TypeError:  # This is a list index
        data = data[int(key)]

print(data)' "$@"
}

Isso tem o benefício de não apenas injetar uma sintaxe no Python, o que pode causar quebra (ou pior, execução arbitrária de código).

Você pode então chamar assim:

json_key hits hits 0 _source memberId < members.json
    
por 21.02.2014 / 07:38
4

Outra alternativa é jq :

$ cat members.json | jq -r '.hits|.hits|.[]|._source|.memberId'
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
    
por 14.06.2015 / 16:57
2

Tente isto:

$ cat json.txt | python -c 'import sys; import simplejson as json; \
print "\n".join( [i["_source"]["memberId"] for i in json.loads( sys.stdin.read() )["hits"]["hits"]] )'

Se você já tem pretty printed json, por que você não apenas grep ?
$ cat json.txt | grep memberId
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcf",
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcG",

Você sempre pode obter um formato bem impresso com o simplejson python para grep it.

# cat json_raw.txt
{"hits": {"hits": [{"_score": 1, "_type": "Medical", "_id": "02:17447847049147026174478:174159", "_source": {"memberLastName": "Dubofsky", "memberMiddleName": "Prayag", "memberId": "0x7b93910446f91928e23e1043dfdf5bcf", "memberFirstName": "Uri"}, "_index": "2000_270_0"}, {"_score": 1, "_type": "Medical", "_id": "02:17447847049147026174478:174159", "_source": {"memberLastName": "Dubofsky", "memberMiddleName": "Prayag", "memberId": "0x7b93910446f91928e23e1043dfdf5bcG", "memberFirstName": "Uri"}, "_index": "2000_270_0"}], "total": 74, "max_score": 1}, "_shards": {"successful": 8, "failed": 0, "total": 8}, "took": 670, "timed_out": false}

Usar dumps:

# cat json_raw.txt | python -c 'import sys; import simplejson as json; \
print json.dumps( json.loads( sys.stdin.read() ), sort_keys=True, indent=4); '

{
    "_shards": {
        "failed": 0,
        "successful": 8,
        "total": 8
    },
    "hits": {
        "hits": [
            {
                "_id": "02:17447847049147026174478:174159",
                "_index": "2000_270_0",
                "_score": 1,
                "_source": {
                    "memberFirstName": "Uri",
                    "memberId": "0x7b93910446f91928e23e1043dfdf5bcf",
                    "memberLastName": "Dubofsky",
                    "memberMiddleName": "Prayag"
                },
                "_type": "Medical"
            },
            {
                "_id": "02:17447847049147026174478:174159",
                "_index": "2000_270_0",
                "_score": 1,
                "_source": {
                    "memberFirstName": "Uri",
                    "memberId": "0x7b93910446f91928e23e1043dfdf5bcG",
                    "memberLastName": "Dubofsky",
                    "memberMiddleName": "Prayag"
                },
                "_type": "Medical"
            }
        ],
        "max_score": 1,
        "total": 74
    },
    "timed_out": false,
    "took": 670
}

Depois disso, basta grep resultar com o padrão 'memberId'.

Para ser completamente preciso:

#!/bin/bash

filename="$1"
cat $filename | python -c 'import sys; import simplejson as json; \
print json.dumps( json.loads( sys.stdin.read() ), sort_keys=True, indent=4)' | \
grep memberId | awk '{print $2}' | sed -e 's/^"//g' | sed -e 's/",$//g'

Uso:

$ bash bash.sh json_raw.txt 
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
    
por 24.02.2014 / 13:05
0

Seguindo este tópico eu usaria o json.tool em python:

python -m json.tool members.json | awk -F'"' '/memberId/{print $4}'

    
por 03.12.2016 / 23:12
0

Usando o deepdiff você não precisa saber as chaves exatas:

import json
from deepdiff import DeepSearch
DeepSearch(json.load(open("members.json", "r")), 'memberId', verbose_level=2)['matched_paths'].values()
    
por 23.05.2017 / 16:56
0

Aqui está uma solução bash.

  1. criar arquivo find_members.sh
  2. adicione a seguinte linha ao arquivo + salvar

    #!/bin/bash
    
    echo -e "\nmemberIds\n---------"
    cat members.json | grep -E 'memberId'|awk '{print$2}' | cut -d '"' -f2
    
  3. chmod +x find_members.sh

Agora execute:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
    
por 28.09.2017 / 06:45