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


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 .


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.


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

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

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


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()

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
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.