¿Analizar JSON usando Python?

18

Tengo un archivo JSON members.jsoncomo se muestra a continuación.

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

Quiero analizarlo usando el bashscript para obtener solo la lista de campos memberId.

El resultado esperado es:

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

Intenté agregar el siguiente 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'"]';
}

Y luego llamó:

$ cat members.json | getJsonVal "memberId"

Pero arroja:

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

Referencia

/programming//a/21595107/432903

prayagupd
fuente
2
¿Por qué necesitas hacer esto en bash? claramente está usando Python aquí, entonces ¿por qué no simplemente crear un script de Python que hace el trabajo? Es posible que no obtenga respuestas reales sobre cómo hacerlo con bash porque cuando necesita hacer eso usa otro idioma.
DavidG
Cambié su título de "usar script bash" a "usar python" ya que python, y no bash, es lo que está usando para analizar json. Por ejemplo, ese error es ciertamente un error de Python, no un error bash.
Ricitos
@goldilocks solo porque su intento se usó python, no significa que su objetivo sea usarpython
jordanm
@DavidG ve mi respuesta. No es puro shell, es un comando externo pero se integra bastante bien en los scripts de shell.
jordanm
¿Puedo sugerirle que elimine la mayoría de los campos irrelevantes en el json? Es suficiente tener 2-3 elementos en _source para obtener la esencia de lo que intentas hacer. El resto solo distrae
Anthon

Respuestas:

25

Si usarías:

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

Puede inspeccionar la estructura del dictonario anidado objy ver que su línea original debería leer:

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

a ese elemento "memberId". De esta manera, puedes mantener Python como un oneliner.

Si hay varios elementos en el elemento "hits" anidado, puede hacer 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
'

La solución de Chris Down es mejor para encontrar un valor único para claves (únicas) en cualquier nivel.

Con mi segundo ejemplo que imprime múltiples valores, estás alcanzando los límites de lo que deberías probar con un solo revestimiento, en ese momento veo pocas razones por las que hacer la mitad del procesamiento en bash, y pasaría a una solución completa de Python .

Anthon
fuente
8

Otra forma de hacer esto en bash es usar jshon . Aquí hay una solución a su problema usando jshon:

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

Las -eopciones extraen valores de json. El -aitera sobre la matriz y -udecodifica la cadena final.

jordanm
fuente
Déjame instalar jshon
prayagupd
6

Bueno, su clave claramente no está en la raíz del objeto. Intenta algo como esto:

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)' "$@"
}

Esto tiene el beneficio de no solo inyectar sintaxis en Python, lo que podría causar la rotura (o peor, la ejecución de código arbitrario).

Entonces puedes llamarlo así:

json_key hits hits 0 _source memberId < members.json
Chris Down
fuente
1
Nota: Esto no recorrerá cada elemento en "hits". Si lo desea, debe escribir un código Python específico para esa instancia.
Chris Down
Pero muestra solo un memberId.
prayagupd
4

Otra alternativa es jq :

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

Prueba esto:

$ 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"]] )'


Si ya tienes pretty printedjson, ¿por qué no lo haces grep?

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

Siempre puede obtener un formato bastante impreso con 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}

Usar volcados:

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

A partir de entonces, simplemente grepresulte con el patrón '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
fuente
0

Siguiendo este hilo , usaría json.tool en python:

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

superk
fuente
0

Usando deepdiff no necesita saber las claves exactas:

import json
from deepdiff import DeepSearch
DeepSearch(json.load(open("members.json", "r")), 'memberId', verbose_level=2)['matched_paths'].values()
serv-inc
fuente
0

Aquí hay una solución bash.

  1. crea un archivo find_members.sh
  2. agregue la siguiente línea al archivo + guardar

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

Ahora ejecútelo:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
Miguel
fuente