I took a stab at implementing a procedure that takes a data structure along with a JSONPath-style selector and a value to be used as the replacement. I'm still learning Tcl, so there's likely a more performant, or idiomatic way of approaching this, but I figured I would share what I had in an attempt to open a dialog about this approach. ====== # pset, short for path-set proc pset {data path val} { set pathList [split $path .] set currPath [first $pathList] set nextPath [join [rest $pathList] .] # puts "data=$data path=$path currPath='$currPath' val=$val" set newdata [switch -regexp -matchvar v -- $currPath { [0-9]+ { if {[llength $data] < $v} { puts "Specified path index $v exceeds size of list [llength $data]." return } # Get the element specified at currPath and continue to process it recursively set selectedData [lindex $data $currPath] # Return the potentially updated data alongside the other non-related elements in the list return [lreplace $data $currPath $currPath [pset $selectedData $nextPath $val]] } [a-zA-Z]+[0-9]* { if {[hasf? $data $currPath]} { #puts "Matching field found." # If there is more to the path, attempt to dig in further to the fields value if {[more? $pathList]} { set fieldValIdx [expr [lsearch $data $currPath] + 1] return [lreplace $data $fieldValIdx $fieldValIdx [pset [getf $data $currPath] $nextPath $val]] } else { # We're at the end of the path. Update the value #puts "Updating '[getf $data $currPath]' to '$val'" return [setf $data $currPath $val] } } else { puts "No matching field found at path." } } }] return $newdata } ## Helper functions below # Get the first item of a list proc first {l} { lindex $l 0 } # Get all but the first elements of a list proc rest {l} { lrange $l 1 end } # Does a list have more than one entry? proc more? {l} { expr [llength $l] > 1 } # "has field" # Searches a list for a field name proc hasf? {l f} { set idx [lsearch $l $f] return [expr $idx > -1] } # "get field" # Searches a list for a field name and returns # the next value in the list (the "value") proc getf {l f} { set idx [lsearch $l $f] if { $idx > -1 } { return [lindex $l [expr $idx + 1]] } } # "set field" # Searches a list for a field name and updates # the next value in the list proc setf {l f v} { set idx [lsearch $l $f] if { $idx > -1 } { set elemAt [expr $idx + 1] return [lreplace $l $elemAt $elemAt $v] } } ====== With the above defined, and data that looks like the following: ====== set data { person1 { name Jim age 33 address {street "333 Third Street"} } person2 { name Bob age 32 address {street "123 Fake Street"} } } ====== We can update Bob's address like so: ====== set data [pset $data person2.address.street "One Eleventy 1st Street"] ====== Or, with data like the following: ====== set data { person { {name Bob} {name Jim} {name Dan} {name Ali} } } ====== We could update the name of the last person in "person" like so: ====== set data [pset $data person.3.name Ally] ======