First a working example with arrays
json_array() {
local -n array="${1}"
readarray -d $'\0' -t array < <(
# Create nul delimited array entry using jq
jq -cjn --argjson arr "$array" '$arr|map(tostring)|.[] "\u0000"'
)
}
> unset arr; arr='["a", "b", "c"]'; json_array arr; echo "${arr[0]} ${arr[1]} ${arr[2]}"
a b c
Now I'm trying to do something similar with dict, convert a json dict into a bash associative array
json_dict() {
local -n dict="${1}"
declare -A hash_table
append_to_hash_table() {
shift
{ read -r key; read -r value; } <<<"$1"
hash_table =([$key]="$value")
}
readarray -d $'\0' -c 1 -C append_to_hash_table < <(
# Create nul delimited dict entry using jq
jq -cjn --argjson d "$dict" '$d|to_entries|map("\(.key)\n\(.value|tostring|@sh)")|.[] "\u0000"'
)
# Here hash_table contain the correct output
dict=""
dict="$hash_table"
}
> unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'; json_dict arr; echo "${arr[@]}"
Nothing
It seems dict="$hash_table"
doesn't correctly update the refname,
How can I make bash dict
refname point to hash_table
?
CodePudding user response:
There's no need for readarray
here: You can have two separate NUL-delimited read
s as part of your while
loop.
See the below answer demonstrated at https://replit.com/@CharlesDuffy2/GrandioseDraftyArguments#main.sh
while IFS= read -r -d '' key && IFS= read -r -d '' value; do
hash_table[$key]=$value
done < <(jq -cjn --argjson d "$arr" \
'$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")')
Putting this into context:
json_dict() {
declare key value in_value="${!1}"
unset "$1" # FIXME: Better to take a $2 for output variable
declare -g -A "$1"
declare -n hash_table="$1"
while IFS= read -r -d '' key && IFS= read -r -d '' value; do
hash_table[$key]=$value
done < <(
jq -cjn --argjson d "$in_value" \
'$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
)
}
arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr
...emits as output:
declare -A arr=([a]="aa" [c]="ccccc" [l]="bb" )
That said, to answer the question exactly as-asked, thus using readarray
:
json_dict() {
declare -a pieces=()
readarray -d '' pieces < <(
jq -cjn --argjson d "${!1}" \
'$d | to_entries[] | ( .key, "\u0000", .value, "\u0000")'
)
unset "$1"
declare -g -A "$1"
declare -n hash_table="$1"
set -- "${pieces[@]}"
while (( $# )); do
hash_table[$1]=$2
{ shift && shift; } || return
done
}
arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
json_dict arr
declare -p arr
CodePudding user response:
Wouldn't it be simpler to just use declare
and have jq
create the contents using @sh
for shell conformity?
Indexed array:
unset arr; arr='["a", "b", "c"]'
declare -a arr="($(jq -r @sh <<< "$arr"))"
Associative array:
unset arr; arr='{"a": "aa", "l": "bb", "c": "ccccc"}'
declare -A arr="($(jq -r 'to_entries[] | @sh "[\(.key)]=\(.value)"' <<< "$arr"))"