I remember that when I worked at Google about a decade ago, there was this common saying:<p>"If the first version of your shell script is more than five lines long, you should have written it in Python."<p>I think there's a lot of truth in that. None of the examples presented in the article look better than had they been written in some existing scripting/programming language. In fact, had they been written in Python or Javascript, it would have been far more obvious what the resulting output would have been, considering that those languages already use {} for objects and [] for lists.<p>For example, take this example:<p><pre><code> jo -p name=JP object=$(jo fruit=Orange point=$(jo x=10 y=20) number=17) sunday=false
</code></pre>
In Python you would write it like this:<p><pre><code> json.dumps({"name": "JP", "object": {"fruit": "Orange", "point": {"x": 10, "y": 20}, "number": 17}, "sunday": False})
</code></pre>
Only a bit more code, but at least it won't suffer from the Norway problem. Even though Python isn't the fastest language out there, it's likely still faster than the shell command above. There is no need to construct a fork bomb just to generate some JSON.
Some time ago I wrote zsh helper [1] that uses jo to quickly construct complex JSON requests, mainly for testing and quering services from console.<p>Paired with httpie [2] aliases [3] it produces concise APL-like syntax:<p><pre><code> POST https://httpbin.org/post test:=j`a=b c=`e=3` l=`*1 2 3``
</code></pre>
Which translates to:<p><pre><code> http POST https://httpbin.org/post test:="$(jo a=b c="$(jo e=3)" l="$(jo -a 1 2 3)")"
</code></pre>
Or, in other words, sending POST with the following body:<p><pre><code> {"a":"b","c":{"e":3},"l":[1,2,3]}
</code></pre>
[1]: <a href="https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019aec6182668e7b571ab4419b6b/.zshrc#L479-L482" rel="nofollow">https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019ae...</a>
[2]: <a href="https://httpie.io/" rel="nofollow">https://httpie.io/</a>
[3]: <a href="https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019aec6182668e7b571ab4419b6b/.zshrc#L821-L828" rel="nofollow">https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019ae...</a>
I like it, very concise but retains all the features you need.<p>`jq` can construct JSON "safely" from shell constructs, but is rather more verbose - e.g. with the same examples:<p><pre><code> $ jq -n --arg name Jane '{"name":$name}'
$ jq -n \
--argjson time $(date +%s) \
--arg dir $HOME \
'{"time":$time,"dir":$dir}'
$ jq -n '$ARGS.positional' --args spring summer winter
$ jq -n \
--arg name JP \
--argjson object "$(
jq -n \
--arg fruit Orange \
--argjson point "$(
jq -n \
--argjson x 10 \
--argjson y 20 \
'{"x":$x,"y":$y}' \
)" \
--argjson number 17 \
'{"fruit":$fruit,"point":$point,"number":$number}' \
)" \
--argjson sunday false \
'{"name":$name,"object":$object,"sunday":$sunday}'</code></pre>
>Bam! Jo tries to be clever about types and knows null, booleans, strings and numbers.<p>I'm very skeptical of this. If I put x=001979 in as a value I dont think I want you trying to guess if that's supposed to be an integer or a string.<p>This sounds like the Norway Problem waiting to happen.
<p><pre><code> > jo normally treats value as a literal string value, unless it begins with one of the following characters:
> value action
> @file substitute the contents of file as-is
> %file substitute the contents of file in base64-encoded form
> :file interpret the contents of file as JSON, and substitute the result
</code></pre>
This is convenient but also very dangerous. This feature will cause the content of an arbitrary file to be embed in the JSON if any value starts with a `@`, `%`, or `:`.<p>This will be a source of bugs or security issues for any script generating json with dynamic values.
This is cool.<p>In the spirit of "do one thing well", I'd so rather use this to construct JSON payloads to curl requests than the curl project's own "json part" proposal[1] under consideration.<p>[1]: <a href="https://github.com/curl/curl/wiki/JSON#--jp-part" rel="nofollow">https://github.com/curl/curl/wiki/JSON#--jp-part</a>
The more I work with JSON, the more I crave some kind of dedicated json editor to easily visualize and manipulate json objects, and to serialize / deserialize strings. This is especially the case with truly massive JSON objects with multiple layers of nesting. Anyway, cool tool that makes one part of the process of whipping up JSON a little less painful
Looks neat, but the documentation was hard to follow. A lot of typos, and some things didn't make sense. For example, I think 2 paragraphs were swapped because the example code below the paragraph taking about using square brackets has no square brackets (but nested objects) while the paragraph right after talks about nested objects but it's example doesn't show it (it does however seemingly demonstrate the square bracket feature mentioned previously)
And I just use Nushell. You have built-ins to create (and parse) not only json but also url, xml and more... <a href="https://github.com/skelly37/Reject-POSUCKS-embrace-Nushell#built-in-parsers" rel="nofollow">https://github.com/skelly37/Reject-POSUCKS-embrace-Nushell#b...</a>
This looks like an informally specified shell-friendly alternative json syntax.<p>I wonder if a formal syntax would help? Perhaps including relevant shell syntax (interpolation, subshell). It could clarify issues, and this different perspective might suggest improvements or different approaches.
There's also jshon which is a simple stack-based DSL for constructing JSON from shell scripts.<p><a href="http://kmkeen.com/jshon/" rel="nofollow">http://kmkeen.com/jshon/</a><p>It's written in C and is not actively developed. The latest commit, it seems, was a pull request from me back in 2018 that fixed a null-termination issue that led to memory corruption.<p>Because I couldn't rely on jshon being correct, I rewrote it in Haskell here:<p><a href="https://github.com/dapphub/dapptools/tree/master/src/jays" rel="nofollow">https://github.com/dapphub/dapptools/tree/master/src/jays</a><p>This is also not developed actively but it's a single simple ~200 line Haskell program.
In the common case where you trust your input entirely you can just interpret your string as JavaScript. Then you don't even need to use quotes for the key names.<p><pre><code> $ alias fooson="node --eval \"console.log(JSON.stringify(eval('(' + process.argv[1] + ')')))\""
$ fooson "{time: $(date +%s), dir: '$HOME'}"
{"time":1457195712,"dir":"/Users/jpm"}
</code></pre>
It may be a bit nicer to place that JavaScript in your path as a node script instead of using an alias.<p><pre><code> #!/usr/bin/env node
console.log(JSON.stringify(eval('(' + process.argv[2] + ')')))
</code></pre>
Since fooson's argument is being interpreted as JavaScript, you can access your environment through process.env. But you could make a slightly easier syntax in various ways. Like with this script:<p><pre><code> #!/usr/bin/env node
for(const [k, v] of Object.entries(process.env)) {
if (!global.hasOwnProperty(k)) {
global[k] = v;
}
}
console.log(JSON.stringify(eval('(' + process.argv[2] + ')')))
</code></pre>
Now environmental variables can be access as if they were JS variables. This can let you handle strings with annoying quoting.<p><pre><code> $ export BAR="\"'''\"\""
$ fooson '{bar: BAR}'
{"bar": "\"'''\"\""}
</code></pre>
If you wanted to do this without trusting your input so much, a JSON dialect where you can use single-quoted strings would get you pretty far.<p><pre><code> $ fooson "{'time': $(date +%s), 'dir': '$HOME'}"
{"time":1457195712,"dir":"/Users/jpm"}
</code></pre>
If you taught the utility to expand env variables itself you'd be able to handle strings with mixed quoting as well.<p><pre><code> $ export BAR="\"'''\"\""
$ fooson '{"bar": "$BAR"}'
{"bar": "\"'''\"\""}
</code></pre>
You'd only need small modifications to a JSON parser to make this work.
Depending on your exact needs, you might also want to try Next Generation Shell. It's a fully featured programming language for the DevOps-y stuff with convenient "print JSON" switch so "ngs -pj YOUR_EXPR" evaluates the expression, serializes the result as JSON and prints it.<p>Disclosure: I'm the author<p>Project link: <a href="https://github.com/ngs-lang/ngs" rel="nofollow">https://github.com/ngs-lang/ngs</a><p>Enjoy!
Dang yo, why it's not " Jo – a shell command to create JSON written in C (2016) (jpmens.net)"<p>You guys do that for "written in Rust"
You can implement this and many other single purpose CLI tools with inline Python or Perl or other language. Easier to remember because it's your favorite language.<p><pre><code> python -c "import json; print(json.dumps(dict(a=10, b=False)))"</code></pre>