Analyser JSON en utilisant Python?

18

J'ai un fichier JSON members.jsoncomme ci-dessous.

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

Je veux l'analyser en utilisant un bashscript pour obtenir uniquement la liste des champs memberId.

Le résultat attendu est:

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

J'ai essayé d'ajouter le code bash + python suivant à .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'"]';
}

Et puis appelé:

$ cat members.json | getJsonVal "memberId"

Mais ça jette:

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

Référence

/programming//a/21595107/432903

Prayagupd
la source
2
Pourquoi avez-vous besoin de faire cela en bash? vous utilisez clairement python ici, alors pourquoi ne pas simplement créer un script python qui fait le travail? Il se peut que vous n'obteniez pas de réponses réelles sur la façon de le faire avec bash, car lorsque vous devez en faire autant, vous utilisez une autre langue.
DavidG
J'ai changé votre titre de "using bash script" en "using python" car python, et non bash, c'est ce que vous utilisez pour analyser json. Par exemple, cette erreur est certainement une erreur python, pas une erreur bash.
goldilocks
@goldilocks juste parce que sa tentative a utilisé python, ne signifie pas que son objectif est d'utiliserpython
jordanm
@DavidG voir ma réponse. Ce n'est pas du shell pur, c'est une commande externe mais elle s'intègre assez bien dans les scripts shell.
jordanm
Puis-je vous suggérer de supprimer la plupart des champs non pertinents dans le json. Il suffit d'avoir 2-3 éléments dans _source pour obtenir l'essentiel de ce que vous essayez de faire. Le reste distrait juste
Anthon

Réponses:

25

Si vous utilisiez:

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

vous pouvez inspecter la structure du dicton imbriqué objet voir que votre ligne d'origine doit se lire:

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

à l'élément "memberId". De cette façon, vous pouvez conserver le Python comme un oneliner.

S'il y a plusieurs éléments dans l'élément "hits" imbriqué, alors vous pouvez faire quelque chose comme:

$ 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 solution de Chris Down est meilleure pour trouver une valeur unique pour des clés (uniques) à n'importe quel niveau.

Avec mon deuxième exemple qui imprime plusieurs valeurs, vous atteignez les limites de ce que vous devriez essayer avec une seule ligne, à ce stade, je vois peu de raisons de faire la moitié du traitement en bash, et je passerais à une solution Python complète .

Anthon
la source
8

Une autre façon de faire cela dans bash est d'utiliser jshon . Voici une solution à votre problème en utilisant jshon:

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

Les -eoptions extraient les valeurs du json. L' -aitère sur le tableau et -udécode la chaîne finale.

jordanm
la source
Permettez-moi d'installer jshon
priagupd
6

Eh bien, votre clé n'est clairement pas à la racine de l'objet. Essayez quelque chose comme ceci:

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

Cela a l'avantage de ne pas simplement injecter de la syntaxe dans Python, ce qui pourrait provoquer une rupture (ou pire, l'exécution de code arbitraire).

Vous pouvez alors l'appeler comme ceci:

json_key hits hits 0 _source memberId < members.json
Chris Down
la source
1
Remarque: Cela ne fera pas de boucle sur chaque élément dans les "hits". Si vous le souhaitez, vous devez écrire du code Python spécifique pour cette instance.
Chris Down
Mais il ne montre qu'un seul memberId.
priagupd
4

Une autre alternative est jq :

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

Essaye ça:

$ 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 vous avez déjà pretty printedjson, pourquoi ne le faites-vous pas grep?

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

Vous pouvez toujours obtenir un joli format imprimé avec du python simplejson 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}

Utilisez les vidages:

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

Ensuite, il vous suffit grepde créer le résultat avec le modèle «memberId».

Pour être tout à fait précis:

#!/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'

Usage:

$ bash bash.sh json_raw.txt 
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
user2496
la source
0

En suivant ce fil, j'utiliserais json.tool en python:

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

superk
la source
0

En utilisant deepdiff, vous n'avez pas besoin de connaître les clés exactes:

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

Voici une solution bash.

  1. créer un fichier find_members.sh
  2. ajouter la ligne suivante au fichier + enregistrer

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

Maintenant, lancez-le:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
Mike
la source