Как добавить записи в объектный файл json в bash

Мне нужно просмотреть словарь и добавить записи в объектный файл json option.json. у меня есть код

for K in "${!MYMAP[@]}"; do 
    opt = "{
        \"OptionName\":\"${K}\",
        \"Value\":\"${MYMAP[$K]}\"
        },"
    echo $opt >> option.json
done 

Но это создает option.json, как показано ниже.

{
    "OptionName": "name",
    "Value": "test"
},
{
    "OptionName": "age",
    "Value": "13"
}

Как мне сделать option.json объектом json и добавить каждый {} внутрь объекта, чтобы мой option.json выглядел так, как показано ниже

[
    {
        "OptionName": "name",
        "Value": "test"
    },
    {
        "OptionName": "age",
        "Value": "13"
    }
]

Обратите внимание, что использование конкатенации строк для создания структурированных данных подвержено ошибкам: как только в вашем значении появятся символы, которые необходимо экранировать, чтобы оно было действительным JSON, вы начнете генерировать недопустимый синтаксис в качестве вывода. Гораздо лучше использовать такой инструмент, как jq, который понимает синтаксис JSON и может выполнять экранирование от вашего имени.

Charles Duffy 01.05.2024 05:53
Как сделать HTTP-запрос в Javascript?
Как сделать HTTP-запрос в Javascript?
В JavaScript вы можете сделать HTTP-запрос, используя объект XMLHttpRequest или более новый API fetch. Вот пример для обоих методов:
1
1
114
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Если вы опустите конечную запятую при первоначальном выводе, вы получите поток входных данных, который jq может собрать в массив с помощью флага --slurp (или -s):

for k in "${!mymap[@]}"; do 
  opt = "{\"OptionName\":\"${k}\", \"Value\":\"${mymap[$k]}\"}"
  echo "$opt"                            # no comma here --^
done | jq -s . > option.json

Однако вы также можете заставить jq сначала выполнять композицию JSON, гарантируя корректную кодировку JSON даже со сложными значениями (такими как двойные кавычки и т. д.). Например, я бы использовал опцию --args для импорта ключей и значений массива bash в качестве позиционных параметров, а встроенную функцию $ARGS для доступа к ним:

declare -A mymap=([name] = "test" [age] = "13")

jq -n '
  $ARGS.positional | [_nwise(length/2)] | transpose
  | map({OptionName: first, Value: last})
' --args "${!mymap[@]}" "${mymap[@]}" > option.json

Как отметил @oguz, новые версии bash также могут расширять массивы до их ключей и значений в желаемом порядке чередования, используя "${mymap[@]@k}". Полный фильтр jq тогда будет просто читать:

jq -n '$ARGS.positional | [_nwise(2) | {OptionName: first, Value: last}]' \
  --args "${mymap[@]@k}"

Новые версии поддерживают bash "${mymap[@]@k}", что устраняет необходимость транспонирования.

oguz ismail 01.05.2024 06:28

@oguz Это хорошее улучшение, спасибо.

pmf 01.05.2024 06:42

Эта функция, очевидно, была добавлена ​​в bash 5.1 (см. unix.stackexchange.com/questions/366581/…).

peak 01.05.2024 08:16

@peak Согласно этому журналу изменений Bash, @K (все пары, цитируемые одним словом) были добавлены в версии 5.1 (см. 5.1.hh), но @k (каждый элемент как отдельные слова, который используется здесь) был добавлен в версии 5.2 ( см. 5.2.т)

pmf 01.05.2024 10:38

Теперь @pmf, если я хочу добавить третий атрибут с опцией ниже: как мне выполнить часть сопоставления, я новичок в jq, поэтому не знаю, как сделать `map({OptionName: first, Value: Last})` для `map({NewAttribute: first, OptionName: Second???, Value: Last})` NewAttribute не является частью jq -n ' $ARGS.positional | [_nwise(length/2)] | transpose | map({OptionName: first, Value: last}) ' --args "${!mymap[@]}" "${mymap[@]}" > option.json, а является просто статическим значением

tanfei12345 01.05.2024 14:14

я пытался использовать nth(1), как показано ниже, объявляйте -A mymap=([name] = "test" [age] = "13") jq -n ' $ARGS.positional | [_nwise(length/3)] | transpose | map({NewAttributes: first, OptionName: nth(1), Value: last}) ' --args "test" "${!MYMAP[@]}" "${MYMAP[@]}" > option.json но результат во втором элементе неправильный ` [ { "NewAttributes": "test", "OptionName": "name", "Value": "test" }, { "NewAttributes": "age", "OptionName": "13", "Value": null } ] `

tanfei12345 01.05.2024 14:40

кажется, я просто жестко запрограммировал новые атрибуты на карте map({"NewAttributes": "NewValue", OptionName: first, Value: last})

tanfei12345 01.05.2024 15:41

@tanfei12345 Чтобы получить статическое значение, попробуйте jq -n '… {NewAttributes: "NewValue", OptionName: first, Value: last} …' --args …. Чтобы импортировать его из оболочки, попробуйте jq -n '… {$NewAttributes, OptionName: first, Value: last} …' --arg NewAttributes "NewValue" --args …. Также ознакомьтесь с руководством . Опции --arg и --args описаны в разделе Вызов jq.

pmf 01.05.2024 20:35

Конечно, лучше использовать jq, если вы работаете с json, но в случае, когда jq недоступен, printf можно использовать, например:

declare -A arr=([key1]=val1 [key2]=val2)

items=$(
    for key in "${!arr[@]}"; {
        val=${arr[$key]}
        printf '{"%s": "%s"},\n' "$key" "$val"
    }
)

echo "[${items%,*}]"
[{"key2": "val2"},
{"key1": "val1"}]

Вот я использую jq для проверки результата:

$ echo "[${items%,*}]" | jq
[
  {
    "key2": "val2"
  },
  {
    "key1": "val1"
  }
]

Обратите внимание на этот синтаксис ${items%,*}, он будет печатать $items без последней запятой, что очень важно для json.

Но да, некоторые хитрые значения, такие как значения из комментариев, могут все испортить, и их необходимо обойти:

declare -A arr=([key1]=val1 [key2]=val2 [key3]='foo "bar \ bar"')

items=$(
    for key in ${!arr[@]}; {
        printf -vval -- '%q' "${arr[$key]}"
        printf -vkey -- '%q'       "$key"
        printf '{"%s": "%s"},\n'  "${key//'\ '/ }" "${val//'\ '/ }"
    }
)

echo "[${items%,*}]" | jq
[
  {
    "key2": "val2"
  },
  {
    "key3": "foo \"bar \\ bar\""
  },
  {
    "key1": "val1"
  }
]

Пока вы не получите значение типа arr[key3]='foo "bar \ bar"', у вас неверный JSON.

glenn jackman 01.05.2024 14:06

Повторная реализация решения Ивана, сохраняющая ваше форматирование -

$: mymap=( [name]=test [age]=13 )
$: fmt='%s  {\n    "OptionName": "%s",\n    "Value": "%s"\n  }'
$: for k in ${!mymap[@]}; do printf -v list "$fmt" "${list:+$list,$'\n'}" "$k" "${mymap[$k]}"; done
$: printf '[\n%s\n]\n' "$list"
[
  {
    "OptionName": "age",
    "Value": "13"
  },
  {
    "OptionName": "name",
    "Value": "test"
  }
]

Но я снова указываю на комментарий Гленна: нечетные данные легко сломать.

Другие вопросы по теме