I am continually frustrated with JSON in TW. If I try hard enough, I can usually get things done, but it’s non-trivial.
You can do it with full paths to your nodes: [<myJSONstring>jsonset[grandpa],[mom],[me],[over21],[yes]]
. But you need to set all intermediate nodes before you add a property to them. The above won’t work unless the object your JSON string represents already has a grandpa
property, which has a mom
property, which has a me
property that is an object:
<$let myJSONstring='{"grandpa": {"mom": {"me": {"name": "Scott"}, "bro": {"name": "Michael"}}}}'>
<pre><code><$text text={{{[<myJSONstring>jsonset[grandpa],[mom],[me],[over21],[yes]] +[format:json[4]] }}} /></code></pre>
</$let>
yielding
{
"grandpa": {
"mom": {
"me": {
"name": "Scott",
"over21": "yes"
},
"bro": {
"name": "Michael"
}
}
}
}
But yes, that is awkward as hell.
This is because of what JSON actually is. It is not a data structure; instead it is a string representation of a data structure. That sounds subtle, but the point is important. Although tiddlers are pretty simple data structures (Hash Maps / Dictionaries), most everything else we deal with in TW is a string or a list of strings.
JSON was designed as a transport format: a way to share nested data structures between different systems. But working with JSON in programming languages usually involves parsing JSON into a more sophisticated data structure on receipt, manipulating that structure as needed, and serializing that data structure back into a string before passing it along. In TW, there’s more friction in this than in many languages. But even if there were no friction, the model would still involve converting from a string, making changes, and converting back.
Mostly to sharpen my TW-JSON skills, I tried this, and the results are not too bad:
<$button>Extract
<$action-createtiddler $basetitle="MyExtract" type="application/json" text="{}" >
<$let tid=<<createTiddler-title>> >
<$list filter=[tag[HelloThere]]>
<$action-setfield $tiddler=<<tid>> $field="text" $value={{{
[<tid>get[text]jsonextract[]]
+[jsonset:object{!!title}]
+[jsonset{!!title},[title],{!!title}]
+[jsonset{!!title},[tags],{!!tags}]
+[jsonset{!!title},[caption],{!!caption}]
+[format:json[4]]
}}} />
</$list>
<$action-navigate $to=<<tid>> />
</$let>
</$action-createtiddler>
</$button>
which yields this:
{
"A Gentle Guide to TiddlyWiki": {
"title": "A Gentle Guide to TiddlyWiki",
"tags": "HelloThere",
"caption": ""
},
"Discover TiddlyWiki": {
"title": "Discover TiddlyWiki",
"tags": "HelloThere",
"caption": ""
},
"Some of the things you can do with TiddlyWiki": {
"title": "Some of the things you can do with TiddlyWiki",
"tags": "HelloThere",
"caption": ""
},
"Ten reasons to switch to TiddlyWiki": {
"title": "Ten reasons to switch to TiddlyWiki",
"tags": "HelloThere",
"caption": ""
},
"Examples": {
"title": "Examples",
"tags": "HelloThere Community",
"caption": ""
},
"What happened to the original TiddlyWiki?": {
"title": "What happened to the original TiddlyWiki?",
"tags": "HelloThere",
"caption": ""
},
"Funding TiddlyWiki": {
"title": "Funding TiddlyWiki",
"tags": "About HelloThere",
"caption": ""
},
"Open Collective": {
"title": "Open Collective",
"tags": "About HelloThere",
"caption": ""
}
}
Obviously it would be easy to parameterize the filter used (here [tag[HelloThere]]
. I’d have to think about how to do dynamic filters, or possibly use reduce
, in order to change the list of fields to include (here title
, tags
, and caption
.) But I’m sure it could be done.
But this is only for a simple, two-level structure. I have some ideas of how to make it simpler to build arbitrary JSON strings from simple parameters. I do it in JS all the time, and so I guess a JS operator might be enough. But I’m trying to figure out how to do it in wikitext.
Extract Tiddlers.tid (676 Bytes)