How to iterate on a dictionary (and an array) from a json data tiddler?

I am a total newbie on the subject. My goal is to create a form to display (then edit) values in a json data tiddler.

My data:

{
"a": "anystring",
"b": { "key1": false, "key2": false, "key3": false },
"c": 0
}

I hit the wall when I want to dynamically display and edit the content indexed by b (the lenght of the dictionnary could vary). I want to create a button per entry (key1, key2, key3) that toggle the associated boolean value when pressed).

To get you started, here’s how to get the values of property b:

<$list filter="[{JSON}jsonextract[b]jsonindexes[]]" variable="index">

<<index>> = {{{ [{JSON}jsonextract[b]jsonget<index>] }}}

</$list>

(JSON is a tiddler containing your JSON)

Try this:

<$list filter="[{MyData}jsonindexes[b]]" variable="key">
   <$let val={{{ [{MyData}jsonget[b],<key>] }}}
         old={{{ [["]] [<key>] [[": ]] [<val>] +[join[]] }}}
         new={{{ [["]] [<key>] [[": ]] [<val>toggle[true],[false]] +[join[]] }}}>
   <$button> toggle <<key>>
      <$action-setfield $tiddler="MyData" text={{{ [{MyData}search-replace<old>,<new>] }}}/>
   </$button>
   </$let>
</$list>

Notes:

  • The $list widget gets the names of the keys in the “b” object (i.e., “key1”, “key2”, “key3”)
  • For each key name:
    • val is the current value of the key
    • old is the current JSON text for the key/value (e.g., "key1": false)
    • new is the new JSON text for the key/value (e.g., "key1": true)
    • Note the use of the toggle[true],[false] filter operator to switch between two values
  • The $button widget shows the key name and, when pressed, replaces the old JSON "key": val with the new JSON "key": val
  • Since there is (currently) no native jsonset operator, we have to use search-replace<old>,<new> to update the JSON content. It’s a bit of a hack and is somewhat brittle with regard to whitespace matching, but it does work.

enjoy,
-e

1 Like

To get around that you can use jsonextract, it seems to reformat the json and trim the whitespaces.

To avoid replacing key/values in other properties, I suggest concataining the whole key before doing a search/replace in the JSON:

<$let key="b" json-key={{{  [{!!JSON}jsonextract<key>] }}}>
<ul>
<$list filter="[<json-key>jsonindexes[]]" variable="index">
<$let
value={{{ [<json-key>jsonget<index>] }}}
old-value={{{ [["]][<index>][[":]][<value>]+[join[]]}}}
new-value={{{ [["]][<index>][[":]][<value>toggle[true],[false]]+[join[]] }}}
old-key={{{ [["]][<key>][[":]][<json-key>]+[join[]] }}} 
new-key={{{ [["]][<key>][[":]][<json-key>search-replace<old-value>,<new-value>]+[join[]] }}}
>
<li><<index>> = <$button set="!!JSON" setTo={{{ [{!!JSON}jsonextract[]search-replace<old-key>,<new-key>] }}}>
<<value>>
</$button></li>
</$let>
</$list>
</ul>
</$let>

https://demos.tiddlyhost.com/#Reading%20and%20modifying%20json

2 Likes

@EricShulman @telumire This is really helpful. Your explanations should be in the documentation.

Knowing the limits and the holes of TW is equally important as the existing features. The missing jsonset operator is a surprise!

You may also want to investigate the JSON Mangler plugin however keep in mind it was designed before the recently added additional JSON operators.

I was just revisiting this thread and wonder of the methods demonstrated by @telumire and @EricShulman could be moved into some macros to provide parametrised access to these basic methods. Currently it is somewhat verbose to use complex JSON data, this could be broken down into a simple subset of “functions”.

  • I am talking about wikitext/widget macros only.