Analisar JSON usando Python?

18

Eu tenho um arquivo JSON members.jsoncomo 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 bashscript obter apenas a lista de campos memberId.

A saída esperada é:

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

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 então chamado:

$ cat members.json | getJsonVal "memberId"

Mas joga:

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

Referência

/programming//a/21595107/432903

prayagupd
fonte
2
Por que você precisa fazer isso no bash? você está claramente usando python aqui; por que não criar um script python que faça o trabalho? Você pode não obter respostas reais sobre como fazer isso com o bash, porque quando você precisa fazer isso, usa outro idioma.
DavidG
Mudei seu título de "using bash script" para "using python"python , pois , e não bash, é o que você está usando para analisar o json. Por exemplo, esse erro é certamente um erro de python, não um erro de bash.
21414 goldilocks
@goldilocks só porque sua tentativa utilizado python, não significa que seu objetivo é usarpython
jordanm
@DavidG veja minha resposta. Não é um shell puro, é um comando externo, mas se integra muito bem a scripts de shell.
Jordanm
Posso sugerir que você remova a maioria dos campos irrelevantes do json. Basta ter 2-3 elementos em _source para entender o que você tenta fazer. O resto só distrai
Anthon

Respostas:

25

Se você usaria:

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

você pode inspecionar a estrutura do dictonary aninhado obje 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". Dessa forma, você pode manter o Python como um oneliner.

Se houver vários elementos no elemento "hits" aninhado, 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 valor único para chaves (exclusivas) em qualquer nível.

No meu segundo exemplo, que imprime vários valores, você está atingindo os limites do que deve tentar com um único liner; nesse ponto, vejo poucas razões para fazer metade do processamento no bash e iria para uma solução completa em Python .

Anthon
fonte
8

Outra maneira de fazer isso no bash é usando 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 -eopções extraem valores do json. Ele -aitera sobre a matriz e -udecodifica a sequência final.

jordanm
fonte
Deixe-me instalar o jshon
prayagupd
6

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

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 sintaxe no Python, o que pode causar interrupção (ou pior, execução arbitrária de código).

Você pode chamá-lo assim:

json_key hits hits 0 _source memberId < members.json
Chris Down
fonte
11
Nota: Isso não passará por cada item em "hits". Se você desejar, escreva um código Python específico para essa instância.
21414 Chris Down
Mas mostra apenas um memberId.
prayagupd
4

Outra alternativa é jq :

$ cat members.json | jq -r '.hits|.hits|.[]|._source|.memberId'
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
hudolejev
fonte
2

Tente o seguinte:

$ 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 printedjson, por que não faz grepisso?

$ cat json.txt | grep memberId
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcf",
               "memberId": "0x7b93910446f91928e23e1043dfdf5bcG",

Você sempre pode obter um formato bastante impresso com o simplejson python grep.

# 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}

Use 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 grepresultar 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
user2496
fonte
0

Após esta discussão, eu usaria o json.tool em python:

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

superk
fonte
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()
serv-inc
fonte
0

Aqui está uma solução bash.

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

    #!/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-o:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
Mike
fonte