Thursday, July 23, 2026

json_shell_utils.tcl


# json_shell_utils.tcl
# General purpose Tcl module for JSON creation and parsing using Linux shell tools `jo` and `jq`.
namespace eval json_shell {
    # -------------------------------------------------------------------------
    # Initialization
    # Check for required external shell commands.
    # -------------------------------------------------------------------------
    proc init {} {
        if {[catch {exec which jo}]} {
            return -code error "Initialization failed: 'jo' command is not installed or not in PATH."
        }
        if {[catch {exec which jq}]} {
            return -code error "Initialization failed: 'jq' command is not installed or not in PATH."
        }
    }
    # Run initialization upon sourcing the file
    init
    # -------------------------------------------------------------------------
    # json_shell::create
    # -------------------------------------------------------------------------
    # Converts a Tcl dictionary into a JSON string using `jo`.
    #
    # Arguments:
    #   dictData - A standard Tcl dictionary of key-value pairs.
    #
    # Returns:
    #   A JSON formatted string.
    # -------------------------------------------------------------------------
    proc create {dictData} {
        set jo_args {}
        dict for {k v} $dictData {
            # Standard jo assignment key=value. 
            # jo automatically handles type inference (numbers, booleans) and escapes strings.
            lappend jo_args "${k}=${v}"
        }
        
        # Safely execute `jo` with all arguments properly expanded
        set cmd [list exec jo {*}$jo_args]
        
        if {[catch {eval $cmd} result]} {
            return -code error "json_shell::create failed: $result"
        }
        
        return $result
    }
    # -------------------------------------------------------------------------
    # json_shell::parse
    # -------------------------------------------------------------------------
    # Converts a JSON string into a native Tcl dictionary using `jq`.
    #
    # Arguments:
    #   json_string - A valid JSON string to parse.
    #
    # Returns:
    #   A Tcl dictionary representing the top-level keys and values.
    # -------------------------------------------------------------------------
    proc parse {json_string} {
        # Return empty dict if string is entirely empty or just whitespace
        if {[string trim $json_string] eq ""} {
            return [dict create]
        }
        # We use `jq` with the -j (join) flag to output keys and values separated by a NULL byte (\0).
        # This completely eliminates issues with newlines, spaces, or quotes inside the JSON values.
        # to_entries converts objects (and arrays to integer-keyed objects), allowing safe iteration.
        set jq_filter {to_entries? | .[]? | .key, "\u0000", (.value|tostring), "\u0000"}
        
        # We pipe the JSON string into standard input of `jq`
        set cmd [list exec jq -j $jq_filter << $json_string]
        
        if {[catch {eval $cmd} result]} {
            return -code error "json_shell::parse failed: $result"
        }
        # If result is empty, return empty dict
        if {$result eq ""} {
            return [dict create]
        }
        # The result ends with a trailing NULL byte because of the filter. Trim it off.
        set result [string trimright $result "\0"]
        
        # Split by NULL byte to get a flat list, which seamlessly casts to a Tcl dictionary.
        set dictList [split $result "\0"]
        
        return $dictList
    }
}

 
 

No comments:

json_shell_utils.tcl