libubox/sh/jshn.sh
Philip Prindeville 6fc29d1c42 jshn.sh: Add pretty-printing to json_dump
If a JSON file might be read by a human, say for debugging, it
could be useful to pretty-print it.  We do this in places by
calling "json_dump -i" but it shouldn't be necessary to know the
arguments to "jshn" (and indeed, that's not portable if we retool
the underlying implementation). Conversely output that's ephemeral
doesn't need to be pretty (say being piped as input to another
command).

Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com>
2023-04-15 15:04:19 +02:00

348 lines
5.9 KiB
Bash

# functions for parsing and generating json
_json_get_var() {
# dest=$1
# var=$2
eval "$1=\"\$${JSON_PREFIX}$2\""
}
_json_set_var() {
# var=$1
local ___val="$2"
eval "${JSON_PREFIX}$1=\"\$___val\""
}
__jshn_raw_append() {
# var=$1
local value="$2"
local sep="${3:- }"
eval "export -- \"$1=\${$1:+\${$1}\${value:+\$sep}}\$value\""
}
_jshn_append() {
# var=$1
local _a_value="$2"
eval "${JSON_PREFIX}$1=\"\${${JSON_PREFIX}$1} \$_a_value\""
}
_get_var() {
# var=$1
# value=$2
eval "$1=\"\$$2\""
}
_set_var() {
# var=$1
local __val="$2"
eval "$1=\"\$__val\""
}
_json_inc() {
# var=$1
# dest=$2
let "${JSON_PREFIX}$1 += 1" "$2 = ${JSON_PREFIX}$1"
}
_json_add_generic() {
# type=$1
# name=$2
# value=$3
# cur=$4
local var
if [ "${4%%[0-9]*}" = "J_A" ]; then
_json_inc "S_$4" var
else
var="${2//[^a-zA-Z0-9_]/_}"
[[ "$var" == "$2" ]] || export -- "${JSON_PREFIX}N_${4}_${var}=$2"
fi
export -- \
"${JSON_PREFIX}${4}_$var=$3" \
"${JSON_PREFIX}T_${4}_$var=$1"
_jshn_append "JSON_UNSET" "${4}_$var"
_jshn_append "K_$4" "$var"
}
_json_add_table() {
# name=$1
# type=$2
# itype=$3
local cur seq
_json_get_var cur JSON_CUR
_json_inc JSON_SEQ seq
local table="J_$3$seq"
_json_set_var "U_$table" "$cur"
export -- "${JSON_PREFIX}K_$table="
unset "${JSON_PREFIX}S_$table"
_json_set_var JSON_CUR "$table"
_jshn_append "JSON_UNSET" "$table"
_json_add_generic "$2" "$1" "$table" "$cur"
}
_json_close_table() {
local _s_cur
_json_get_var _s_cur JSON_CUR
_json_get_var "${JSON_PREFIX}JSON_CUR" "U_$_s_cur"
}
json_set_namespace() {
local _new="$1"
local _old="$2"
[ -n "$_old" ] && _set_var "$_old" "$JSON_PREFIX"
JSON_PREFIX="$_new"
}
json_cleanup() {
local unset tmp
_json_get_var unset JSON_UNSET
for tmp in $unset J_V; do
unset \
${JSON_PREFIX}U_$tmp \
${JSON_PREFIX}K_$tmp \
${JSON_PREFIX}S_$tmp \
${JSON_PREFIX}T_$tmp \
${JSON_PREFIX}N_$tmp \
${JSON_PREFIX}$tmp
done
unset \
${JSON_PREFIX}JSON_SEQ \
${JSON_PREFIX}JSON_CUR \
${JSON_PREFIX}JSON_UNSET
}
json_init() {
json_cleanup
export -n ${JSON_PREFIX}JSON_SEQ=0
export -- \
${JSON_PREFIX}JSON_CUR="J_V" \
${JSON_PREFIX}K_J_V=
}
json_add_object() {
_json_add_table "$1" object T
}
json_close_object() {
_json_close_table
}
json_add_array() {
_json_add_table "$1" array A
}
json_close_array() {
_json_close_table
}
json_add_string() {
local cur
_json_get_var cur JSON_CUR
_json_add_generic string "$1" "$2" "$cur"
}
json_add_int() {
local cur
_json_get_var cur JSON_CUR
_json_add_generic int "$1" "$2" "$cur"
}
json_add_boolean() {
local cur
_json_get_var cur JSON_CUR
_json_add_generic boolean "$1" "$2" "$cur"
}
json_add_double() {
local cur
_json_get_var cur JSON_CUR
_json_add_generic double "$1" "$2" "$cur"
}
json_add_null() {
local cur
_json_get_var cur JSON_CUR
_json_add_generic null "$1" "" "$cur"
}
json_add_fields() {
while [ "$#" -gt 0 ]; do
local field="$1"
shift
local name="${field%%=*}"
local val="${field#*=}"
[ "$name" != "$val" ] || val=""
local type="${name#*:}"
[ "$type" != "$name" ] || type=string
name="${name%%:*}"
case "$type" in
string|int|boolean|double)
local cur
_json_get_var cur JSON_CUR
_json_add_generic "$type" "$name" "$val" "$cur"
;;
esac
done
}
# functions read access to json variables
json_compact() {
JSON_NONEWLINE=1
JSON_INDENT=
}
json_pretty() {
JSON_NONEWLINE=
JSON_INDENT=1
}
json_load() {
eval "`jshn -r "$1"`"
}
json_load_file() {
eval "`jshn -R "$1"`"
}
json_dump() {
jshn "$@" ${JSON_PREFIX:+-p "$JSON_PREFIX"} ${JSON_NONEWLINE:+-n} ${JSON_INDENT:+-i} -w
}
json_get_type() {
local __dest="$1"
local __cur
_json_get_var __cur JSON_CUR
local __var="${JSON_PREFIX}T_${__cur}_${2//[^a-zA-Z0-9_]/_}"
eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]"
}
json_get_keys() {
local __dest="$1"
local _tbl_cur
if [ -n "$2" ]; then
json_get_var _tbl_cur "$2"
else
_json_get_var _tbl_cur JSON_CUR
fi
local __var="${JSON_PREFIX}K_${_tbl_cur}"
eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]"
}
json_get_values() {
local _v_dest="$1"
local _v_keys _v_val _select=
local _json_no_warning=1
unset "$_v_dest"
[ -n "$2" ] && {
json_select "$2" || return 1
_select=1
}
json_get_keys _v_keys
set -- $_v_keys
while [ "$#" -gt 0 ]; do
json_get_var _v_val "$1"
__jshn_raw_append "$_v_dest" "$_v_val"
shift
done
[ -n "$_select" ] && json_select ..
return 0
}
json_get_var() {
local __dest="$1"
local __cur
_json_get_var __cur JSON_CUR
local __var="${JSON_PREFIX}${__cur}_${2//[^a-zA-Z0-9_]/_}"
eval "export -- \"$__dest=\${$__var:-$3}\"; [ -n \"\${$__var+x}\${3+x}\" ]"
}
json_get_vars() {
while [ "$#" -gt 0 ]; do
local _var="$1"; shift
if [ "$_var" != "${_var#*:}" ]; then
json_get_var "${_var%%:*}" "${_var%%:*}" "${_var#*:}"
else
json_get_var "$_var" "$_var"
fi
done
}
json_select() {
local target="$1"
local type
local cur
[ -z "$1" ] && {
_json_set_var JSON_CUR "J_V"
return 0
}
[[ "$1" == ".." ]] && {
_json_get_var cur JSON_CUR
_json_get_var cur "U_$cur"
_json_set_var JSON_CUR "$cur"
return 0
}
json_get_type type "$target"
case "$type" in
object|array)
json_get_var cur "$target"
_json_set_var JSON_CUR "$cur"
;;
*)
[ -n "$_json_no_warning" ] || \
echo "WARNING: Variable '$target' does not exist or is not an array/object"
return 1
;;
esac
}
json_is_a() {
local type
json_get_type type "$1"
[ "$type" = "$2" ]
}
json_for_each_item() {
[ "$#" -ge 2 ] || return 0
local function="$1"; shift
local target="$1"; shift
local type val
json_get_type type "$target"
case "$type" in
object|array)
local keys key
json_select "$target"
json_get_keys keys
for key in $keys; do
json_get_var val "$key"
eval "$function \"\$val\" \"\$key\" \"\$@\""
done
json_select ..
;;
*)
json_get_var val "$target"
eval "$function \"\$val\" \"\" \"\$@\""
;;
esac
}