From 318ba9094e4e49c87245d2c770010bf6048bf736 Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Wed, 7 Jul 2021 09:18:03 +0900 Subject: [PATCH] include/json: Introduce type specifiers When constructing JSON objects and arrays, there are cases where the json_object() and json_array() functions cannot correctly determine the type of a variable (for example, "true" could be string or bool). This commit introduces type specifiers that allow the user to declare the type of a value that is passed to the json functions. Specifiers are prepended to the value when passing it to the json_object() json_array() functions. The following specifiers are understood: - "s:" string - "i:" integer - "b:" boolean - "f:" float - "o:" object - "a:" array The following is how the specifiers are passed. json_object "some_string" "s:$some_string" \ "some_integer" "i:$some_integer" For backwards compatibility, the specifiers may be omitted. In that case, a best-effort guess will be made. Omitting type specifiers will make it impossible to pass certain values in strings. --- include/json.sh | 215 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 154 insertions(+), 61 deletions(-) diff --git a/include/json.sh b/include/json.sh index b60da1d..af81d18 100644 --- a/include/json.sh +++ b/include/json.sh @@ -17,67 +17,172 @@ # along with this program. If not, see . __init() { - if ! include "log"; then + if ! include "log" "is" "array"; then return 1 fi + declare -gxir __json_type_integer=0 + declare -gxir __json_type_string=1 + declare -gxir __json_type_bool=2 + declare -gxir __json_type_float=3 + declare -gxir __json_type_object=4 + declare -gxir __json_type_array=5 + return 0 } -json_object() { - local args=("$@") +_json_guess_type() { + local input="$1" + + local re_number + local re_float + local re_object + local re_array + + re_number='^[-+]{,1}[0-9]+$' + re_float='^[-+]{,1}([0-9]*\.[0-9]+|[0-9]+\.[0-9]*)$' + re_object='^\{.*\}$' + re_array='^\[.*\]$' + + if [[ "$input" =~ $re_number ]]; then + echo "$__json_type_integer" + + elif [[ "$input" =~ $re_object ]]; then + echo "$__json_type_object" - local i - local nvps + elif [[ "$input" =~ $re_array ]]; then + echo "$__json_type_array" + + elif [[ "$input" =~ $re_float ]]; then + echo "$__json_type_float" + + elif [[ "$input" == "true" ]] || + [[ "$input" == "false" ]]; then + echo "$__json_type_bool" + + else + echo "$__json_type_string" + fi + + return 0 +} + +_json_get_type() { + local input="$1" + + declare -A type_map + local type_prefix + local type + local output + + type_map["s:"]="$__json_type_string" + type_map["i:"]="$__json_type_integer" + type_map["b:"]="$__json_type_bool" + type_map["f:"]="$__json_type_float" + type_map["o:"]="$__json_type_object" + type_map["a:"]="$__json_type_array" + + type_prefix="${input:0:2}" + + if array_contains "$type_prefix" "${!type_map[@]}"; then + type="${type_map[$type_prefix]}" + output="${input:2}" + else + type=$(_json_guess_type "$input") + output="$input" + fi + + echo "$output" + return "$type" +} - nvps=0 +_json_print_value() { + local value="$1" + + local value_raw + local output + + value_raw=$(_json_get_type "$value") + case "$?" in + "$__json_type_integer") + if ! output=$(printf '%d' "$value_raw"); then + return 1 + fi + ;; + + "$__json_type_float") + if ! output=$(printf '%f' "$value_raw"); then + return 1 + fi + ;; + + "$__json_type_bool"|"$__json_type_object"|"$__json_type_array") + if ! output=$(printf '%s' "$value_raw"); then + return 1 + fi + ;; + + "$__json_type_string") + if ! output=$(printf '"%s"' "$value_raw"); then + return 1 + fi + ;; + + *) + return 1 + ;; + esac + + echo "$output" + return 0 +} - if (( ${#args[@]} % 2 != 0 )); then - log_error "Invalid number of arguments" - return 1 - fi +json_object() { + local args=("$@") - printf "{" - for (( i = 0; i < ${#args[@]}; i++ )); do - local name - local value + local i + local nvps + local output - local re_number - local re_object - local re_array + nvps=0 + output="{" - re_number='^[0-9]+$' - re_object='^\{.*\}$' - re_array='^\[.*\]$' + if (( ${#args[@]} % 2 != 0 )); then + log_error "Invalid number of arguments" + return 1 + fi - name="${args[$i]}" - ((i++)) - value="${args[$i]}" + for (( i = 0; i < ${#args[@]}; i++ )); do + local name + local value + local value_raw - if [ -z "$name" ] || [ -z "$value" ]; then - continue - fi + name="${args[$i]}" + ((i++)) + value="${args[$i]}" - if (( nvps > 0 )); then - printf ', ' - fi + if [ -z "$name" ] || [ -z "$value" ]; then + continue + fi - if [[ "$value" =~ $re_number ]]; then - printf '"%s": %d' "$name" "$value" + if (( nvps > 0 )); then + output+=", " + fi - elif [[ "$value" =~ $re_object ]] || - [[ "$value" =~ $re_array ]]; then - printf '"%s": %s' "$name" "$value" + if ! value_raw=$(_json_print_value "$value"); then + return 1 + fi - else - printf '"%s": "%s"' "$name" "$value" - fi + if ! output+=$(printf '"%s": %s' "$name" "$value_raw"); then + return 1 + fi - ((nvps++)) - done - printf "}\n" + ((nvps++)) + done + output+="}" - return 0 + printf "%s\n" "$output" + return 0 } json_object_get() { @@ -99,18 +204,11 @@ json_object_get() { json_array() { local args=("$@") + local output local arg local n - local re_number - local re_object - local re_array - - re_number='^[0-9]+$' - re_object='^\{.*\}$' - re_array='^\[.*\]$' - - printf "[" + output="[" n=0 for arg in "${args[@]}"; do @@ -119,23 +217,18 @@ json_array() { fi if (( n > 0 )); then - printf ", " + output+=", " fi - if [[ "$arg" =~ $re_number ]]; then - printf '%d' "$arg" - - elif [[ "$arg" =~ $re_object ]] || - [[ "$arg" =~ $re_array ]]; then - printf '%s' "$arg" - - else - printf '"%s"' "$arg" + if ! output+=$(_json_print_value "$arg"); then + return 1 fi ((n++)) done - printf "]\n" + output+="]" + + printf "%s\n" "$output" return 0 } -- 2.47.3