jq: Проверка наличия ключа в списке предопределенных ключей

У меня есть случай, когда мне нужно разобрать цитируемый JSON в JSON. Я знаю, какие необязательные атрибуты будут содержать JSON в кавычках, а какие нет. Поэтому я хочу проверить, есть ли ключи атрибутов в списке возможных ключей. У меня уже есть следующее:

# attributes "a" and "b" contain quoted JSON
echo '{"a":"{\"x\":1}","y":2}' |
jq -c '
   def is_json($o): ["a","b"] | (map(select(. == $o)) | length) > 0;
   with_entries(if is_json(.key) then .value = (.value|fromjson) else . end)
'

Это уже дает желаемый результат: {"a":{"x":1},"y":2}. Однако проверка имени атрибута выглядит неуклюже, учитывая, что jq предоставляет множество встроенных функций, таких как has, in, contains, inside и т. д.

Вопрос. Есть ли лучший способ проверить, находится ли ключ атрибута в заданном списке?

Изменить: вот текущее решение, основанное на ответе Пика.

#!/bin/bash
to_array_string() { echo "$*" | awk -v OFS='","' 'NF > 0 {$1=$1; print "\""$0"\""}'; }
to_json_array_string() { echo "["`to_array_string "$@"`"]"; }

parse_json_jq() { jq -c "
   reduce keys[] as \$key
   (.; if any((`to_array_string "$@"`); . == \$key) and .[\$key] != null then .[\$key] |= fromjson else . end)
";}

person Juve    schedule 19.10.2016    source источник


Ответы (1)


Есть три способа улучшить вашу программу:

  1. (эффективность) избежание создания ненужного массива (в is_json);
  2. (эффективность) использование семантики «короткого замыкания», чтобы избежать ненужных итераций;
  3. (эффективность) избегать построения/деконструкции, связанной с with_entries;

По большей части, я думаю, вы согласитесь, что предлагаемые здесь альтернативы проще, лаконичнее и читабельнее.

Если у вас версия jq 1.5 или новее, основные улучшения можно получить с помощью any/2:

def is_json($o): any( ("a","b"); . == $o );

with_entries(if is_json(.key) then .value |= fromjson else . end)

Обратите также внимание на использование «|=» вместо «=».

Если в вашем jq нет any/2, вы можете использовать следующее определение, хотя в нем отсутствует семантика короткого замыкания:

def any(s): reduce s as $i (false; . == true or $i);

Наконец, чтобы избежать использования with_entries, вы можете использовать reduce и полностью исключить is_json:

reduce keys[] as $key
  (.; if any(("a","b"); . == $key) then .[$key] |= fromjson else . end)
person peak    schedule 19.10.2016
comment
Спасибо за это решение. Мне просто нужно было добавить еще одну проверку null, чтобы она работала (см. редактирование вопроса). - person Juve; 26.10.2016