Разобрать JSON с помощью Python?

18

У меня есть файл JSON, members.jsonкак показано ниже.

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

Я хочу разобрать его с помощью bashскрипта, получить только список полей memberId.

Ожидаемый результат:

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

Я попытался добавить следующий код bash + python .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'"]';
}

А потом позвонил:

$ cat members.json | getJsonVal "memberId"

Но это бросает:

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

Ссылка

/programming//a/21595107/432903

prayagupd
источник
2
Зачем тебе это делать в bash? Вы явно используете Python здесь, так почему бы просто не создать сценарий Python, который делает эту работу? Возможно, вы не получите реальных ответов о том, как это сделать с помощью bash, потому что, когда вам нужно сделать так много, вы используете другой язык.
DavidG
Я изменил ваш заголовок с «использование скрипта bash» на «использование python», поскольку python, а не то bash, что вы используете для анализа json. Например, эта ошибка, безусловно, является ошибкой Python, а не ошибкой bash.
Златовласка
@goldilocks только потому , что использовал его попытка python, не означает , что его цель заключается в использованииpython
jordanm
@DavidG посмотри мой ответ. Это не чистая оболочка, это внешняя команда, но она довольно хорошо интегрируется в сценарии оболочки.
Иордания
Могу ли я предложить вам убрать большинство не относящихся к делу полей в JSON. Достаточно иметь 2-3 элемента в _source, чтобы понять суть того, что вы пытаетесь сделать. Остальное просто отвлекает
Anthon

Ответы:

25

Если бы вы использовали:

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

Вы можете проверить структуру вложенного диктонара objи увидеть, что ваша оригинальная строка должна выглядеть так:

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

к этому элементу "memberId". Таким образом, вы можете сохранить Python как единственного пользователя.

Если во вложенном элементе «hit» есть несколько элементов, вы можете сделать что-то вроде:

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

Решение Криса Дауна лучше для поиска единственного значения (уникальных) ключей на любом уровне.

С моим вторым примером, который печатает несколько значений, вы выходите за пределы того, что вы должны попробовать с одним вкладышем, в этот момент я вижу мало причин, почему нужно выполнять половину обработки в bash, и вы бы перешли к полному решению Python ,

Энтон
источник
8

Другой способ сделать это в bash - использовать jshon . Вот решение вашей проблемы с помощью jshon:

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

В -eопции извлечения значений из JSON. -aПеребирает массив и -uдекодирует конечную строку.

jordanm
источник
Дайте мне установить jshon
prayagupd
6

Что ж, ваш ключ совершенно явно не в корне объекта. Попробуйте что-то вроде этого:

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

Преимущество этого состоит не просто в том, чтобы вводить синтаксис в Python, что может привести к поломке (или, что еще хуже, к выполнению произвольного кода).

Затем вы можете назвать это так:

json_key hits hits 0 _source memberId < members.json
Крис Даун
источник
1
Примечание: это не будет зацикливаться на каждом элементе в «хитах». Если вы хотите этого, вы должны написать специальный код Python для этого экземпляра.
Крис Даун
Но он показывает только один memberId.
prayagupd
4

Другой альтернативой является JQ :

$ cat members.json | jq -r '.hits|.hits|.[]|._source|.memberId'
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
hudolejev
источник
2

Попробуй это:

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


Если у вас уже есть pretty printedJSON, почему бы вам не просто grep?

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

Вы всегда можете получить симпатичный печатный формат с 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}

Используйте дампы:

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

После этого просто grepрезультат с шаблоном 'memberId'.

Чтобы быть полностью точным:

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

Использование:

$ bash bash.sh json_raw.txt 
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
user2496
источник
0

Следуя этой теме, я бы использовал json.tool в python:

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

superk
источник
0

При использовании deepdiff вам не нужно знать точные ключи:

import json
from deepdiff import DeepSearch
DeepSearch(json.load(open("members.json", "r")), 'memberId', verbose_level=2)['matched_paths'].values()
Serv-вкл
источник
0

Вот решение Bash.

  1. создать файл find_members.sh
  2. добавить следующую строку в файл + сохранить

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

Теперь запустите это:

$ ./find_members.sh

memberIds
----------------
0x7b93910446f91928e23e1043dfdf5bcf
0x7b93910446f91928e23e1043dfdf5bcG
Майк
источник