Analizować JSON za pomocą Pythona?

18

Mam plik JSON, members.jsonjak poniżej.

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

Chcę to przeanalizować za pomocą bashskryptu, aby uzyskać tylko listę pól memberId.

Oczekiwany wynik to:

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

Próbowałem dodać następujący kod bash + python do .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'"]';
}

A potem nazwany:

$ cat members.json | getJsonVal "memberId"

Ale rzuca:

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

Odniesienie

/programming//a/21595107/432903

prayagupd
źródło
2
Dlaczego musisz to zrobić w bash? wyraźnie używasz tutaj Pythona, więc dlaczego po prostu nie utworzyć skryptu Python, który wykona zadanie? Możesz nie uzyskać rzeczywistych odpowiedzi na temat tego, jak to zrobić za pomocą bash, ponieważ kiedy musisz to zrobić, używasz innego języka.
DavidG
Zmieniłem twój tytuł z „za pomocą skryptu bash” na „za pomocą Pythona”, ponieważ python, nie bash, to jest to, czego używasz do parsowania Jsona. Np. Błąd ten z pewnością jest błędem w Pythonie, a nie błędem bash.
goldilocks
@Goldilocks tylko dlatego, że jego próba została użyta python, nie znaczy, że jego celem jest użyciepython
jordanm
@DavidG zobacz moją odpowiedź. To nie jest czysta powłoka, to polecenie zewnętrzne, ale całkiem dobrze integruje się ze skryptami powłoki.
jordanm
Czy mogę zasugerować usunięcie większości nieistotnych pól w Json. Wystarczy mieć 2-3 elementy w _source, aby uzyskać sedno tego, co próbujesz zrobić. Reszta tylko rozprasza
Anthon

Odpowiedzi:

25

Jeśli chcesz użyć:

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

możesz sprawdzić strukturę zagnieżdżonego słownika obji zobaczyć, czy w oryginalnej linii powinno znajdować się:

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

do tego elementu „memberId”. W ten sposób możesz zachować Python jako oneliner.

Jeśli w zagnieżdżonym elemencie „trafienia” znajduje się wiele elementów, możesz zrobić coś takiego:

$ 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
'

Rozwiązanie Chrisa Down jest lepsze do znajdowania pojedynczej wartości (unikatowych) kluczy na dowolnym poziomie.

W moim drugim przykładzie, który wypisuje wiele wartości, osiągasz granice tego, co powinieneś wypróbować za pomocą jednej linijki, w tym momencie nie widzę powodu, aby robić połowę przetwarzania w bash, i chciałbym przejść do kompletnego rozwiązania Python .

Anthon
źródło
8

Innym sposobem na zrobienie tego w bash jest użycie jshon . Oto rozwiązanie Twojego problemu za pomocą jshon:

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

Te -eopcje wyodrębnić wartości z JSON. -aIteracje nad tablicy i -udekoduje końcowy ciąg.

Jordan
źródło
Pozwól mi zainstalować jshon
prayagupd
6

Cóż, twój klucz nie jest najwyraźniej u podstawy obiektu. Wypróbuj coś takiego:

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

Ma to tę zaletę, że nie tylko po prostu wstrzykuje składnię do Pythona, co może spowodować uszkodzenie (lub, co gorsza, wykonanie dowolnego kodu).

Możesz to tak nazwać:

json_key hits hits 0 _source memberId < members.json
Chris Down
źródło
1
Uwaga: To nie będzie zapętlać każdego elementu w „hitach”. Jeśli chcesz, powinieneś napisać konkretny kod Python dla tej instancji.
Chris Down
Ale pokazuje tylko jeden element członkowski.
prayagupd
4

Inną alternatywą jest jq :

$ cat members.json | jq -r '.hits|.hits|.[]|._source|.memberId'
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
hudolejev
źródło
2

Spróbuj tego:

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


Jeśli masz już pretty printedJSON, dlaczego tego nie zrobisz grep?

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

Zawsze możesz uzyskać ładny wydrukowany format z pythonem 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}

Użyj zrzutów:

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

Następnie po prostu grepuzyskaj wynik za pomocą wzoru „memberId”.

Aby być całkowicie precyzyjnym:

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

Stosowanie:

$ bash bash.sh json_raw.txt 
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
użytkownik2496
źródło
0

Po tym wątku użyłbym json.tool w python:

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

superk
źródło
0

Korzystając z deepdiff , nie musisz znać dokładnych kluczy:

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

Oto rozwiązanie bash.

  1. utwórz plik find_members.sh
  2. dodaj następujący wiersz do pliku + zapisz

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

Teraz uruchom to:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
mikrofon
źródło