7 ms·
Jo – a shell command to create JSON (2016)
- mirekrusin 5y agoCould be simply called `json`.
- peakaboo 5y agoGood luck in search results.
- tedmiston 5y agoDirect link to repo: https://github.com/jpmens/jo https://github.com/jpmens/jo
- visarga 5y agoYou 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. python -c "import json; print(json.dumps(dict(a=10, b=False)))"
- Too 5y agoWhile a handy trick, it doesn't entirely solve the original problem when the values have dynamic content. In your example, replace 10 with $FOO and then you are back to square one, having to escape python-strings within a shell-string. Better avoid the problem entirely by not using shell to begin with. To instead continue on the dirty track, replace 10 with int(sys.argv[1]) and call it as python -c "..." $FOO.
- Shadonototra 5y agoDang yo, why it's not " Jo – a shell command to create JSON written in C (2016) (jpmens.net)" You guys do that for "written in Rust"
- avgcorrection 5y agoThe original title does not contain “written in C”. Do moderators usually rewrite the title to add things like “written in Rust”?
- higgins 5y agonext to "copy as cURL" in dev tools, this might make the regular rotation
- borgchick 5y agoAwesome, thank you. Lovely counterpart to jq :)
- videogreg93 5y agoLooks 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)
- montroser 5y agoThis is cool. 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. [1]: https://github.com/curl/curl/wiki/JSON#--jp-part https://github.com/curl/curl/wiki/JSON#--jp-part
- matja 5y agoAgree, I was surprised that the cURL feature was considered as it seems to go against the "Do One Thing" and composability points of the UNIX philosophy.
- nerdponx 5y agoCurl does like 100 "things" already by that standard. The Unix philosophy doesn't have to be reductionist. Curl does do one thing: make network requests. This feature is making it easier to make network requests, i.e. it makes it better at doing the one thing that it does.
- oblio 5y agocurl? Do one thing? In the same sentence? curl looks like it has hundreds of flags. https://danluu.com/cli-complexity/ https://danluu.com/cli-complexity/
- deleted 5y ago[deleted]
- seletskiy 5y agoSome time ago I wrote zsh helper [1] that uses jo to quickly construct complex JSON requests, mainly for testing and quering services from console. Paired with httpie [2] aliases [3] it produces concise APL-like syntax: POST https://httpbin.org/post test:=j`a=b c=`e=3` l=`*1 2 3`` Which translates to: http POST https://httpbin.org/post test:="$(jo a=b c="$(jo e=3)" l="$(jo -a 1 2 3)")" Or, in other words, sending POST with the following body: {"a":"b","c":{"e":3},"l":[1,2,3]} [1]: https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019aec6182668e7b571ab4419b6b/.zshrc#L479-L482 https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019ae... [2]: https://httpie.io/ https://httpie.io/ [3]: https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019aec6182668e7b571ab4419b6b/.zshrc#L821-L828 https://github.com/seletskiy/dotfiles/blob/78ac45c01bdf019ae...
- jkbr 5y agoHTTPie creator here. We’ve recently[0] added support for nested JSON[1] to the HTTPie request language, so you can now craft complex JSON request directly: $ http pie.dev/post test[a]=b test[c][e]:=3 test[l][]:=1 [0] https://httpie.io/blog/httpie-3.0.0 https://httpie.io/blog/httpie-3.0.0 [1] https://httpie.io/docs/cli/nested-json https://httpie.io/docs/cli/nested-json
- timwis 5y agojo immediately reminded me of httpie’s CLI syntax. Is it just a coincidence, or does httpie use jo under the hood? Btw loving your new desktop app so far!
- jkbr 5y agoWe did look at `jo`, and also `jarg`[0], the W3C HTML JSON form syntax[1], and pretty much every other approach we could find. We had quite a few requirements that the new nested syntax had to meet: be simple/flexible, easy to read/write, backward/forward compatible, and play well with the rest of the HTTPie request language. The final syntax is heavily inspired by the HTML JSON forms one. But we made it stricter, added type safety, and some other features. It also supports all the existing functionality like embedding raw JSON strings and JSON/text files by paths. The final implementation[2] is completely custom. We have plans to ship it as a standalone tool as well, publish the test suite in a language-independent format and write a formal spec so that other tools can easily adopt it. This spec will eventually be a subset of one for the overall HTTPie request language, which is currently tied to our CLI implementation but we want to decouple it. Happy to hear you like the desktop app! [0] https://github.com/jdp/jarg https://github.com/jdp/jarg [1] https://www.w3.org/TR/html-json-forms/ https://www.w3.org/TR/html-json-forms/ [2] https://github.com/httpie/httpie/blob/master/httpie/cli/nested_json.py https://github.com/httpie/httpie/blob/master/httpie/cli/nest... — this is the path parser
- matja 5y agoI like it, very concise but retains all the features you need. `jq` can construct JSON "safely" from shell constructs, but is rather more verbose - e.g. with the same examples: $ 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}'
- mbrock 5y agoThere's also jshon which is a simple stack-based DSL for constructing JSON from shell scripts. http://kmkeen.com/jshon/ http://kmkeen.com/jshon/ 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. Because I couldn't rely on jshon being correct, I rewrote it in Haskell here: https://github.com/dapphub/dapptools/tree/master/src/jays https://github.com/dapphub/dapptools/tree/master/src/jays This is also not developed actively but it's a single simple ~200 line Haskell program.
- lordleft 5y agoThe 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
- fiddlerwoaroof 5y agoI wrote something like this for emacs: a couple functions “fwoar/dive” and “fwoar/return” that let you navigate JSON documents in a two-pane sort of paradigm. https://youtu.be/qbRNmk-malw https://youtu.be/qbRNmk-malw
- qbasic_forever 5y agoAdd a json LSP to your editor, or use VS code which includes it natively: https://www.npmjs.com/package/vscode-json-languageserver https://www.npmjs.com/package/vscode-json-languageserver Configure a json schema for the document you're editing and suddenly you get suggestions, validation, etc. as you type. It's pretty magical. There's one for yaml too that works well in my experience: https://github.com/redhat-developer/yaml-language-server https://github.com/redhat-developer/yaml-language-server
- 8organicbits 5y agoI built a JSON editor for Android a while back as part of a tool for kicking off AWS Lambda functions. I was planning on pulling the JSON editor to it's own reusable package, but lost momentum. I imagine it could be useful in many sorts of apps that use JSON. https://play.google.com/store/apps/details?id=com.alexsci.android.lambdarunner https://play.google.com/store/apps/details?id=com.alexsci.an... https://github.com/ralexander-phi/android-aws-lambda-runner https://github.com/ralexander-phi/android-aws-lambda-runner
- pwdisswordfish9 5y agoDoes it suffer from the Norway problem?
- halostatue 5y agoIt does not. jo -- -b foo=no -s a=12 q=[1,2,3] {"foo":true,"a":"12","q":[1,2,3]}
- 0xbadcafebee 5y agoI think you're thinking of YAML; JSON doesn't interpret "no" as a boolean. Scroll down here to see the JSON grammar: https://www.crockford.com/mckeeman.html https://www.crockford.com/mckeeman.html I actually think the "Norway problem" is a PEBKAC from users not learning the data format. But this tool may confuse some people or applications who don't know what a boolean, integer, float or string are, and try to mix types when the program reading them wasn't designed to. Probably the issue will come up whenever people mix different kinds of versions ("1", "1.1", "1.1.1" should be parsed as an int, float, and string, respectively)
- motoboi 5y agojo a=12 Should “a” be number or a string in the resulting JSON? And if it’s number, how can I tell it to output a string?
- ysk73 5y agoAnd I just use Nushell. You have built-ins to create (and parse) not only json but also url, xml and more... https://github.com/skelly37/Reject-POSUCKS-embrace-Nushell#built-in-parsers https://github.com/skelly37/Reject-POSUCKS-embrace-Nushell#b...
- pydry 5y ago>Bam! Jo tries to be clever about types and knows null, booleans, strings and numbers. 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. This sounds like the Norway Problem waiting to happen.
- matja 5y agothen use -s : jo -- -s opaque_id=001979 {"opaque_id":"001979"}
- pydry 5y agojo opaque_id=$(command used in prod that returns digits + letters for three months and everything works just fine and then suddenly at 3am on a Sunday it returns 1979 and breaks everything) Avoid using this command in prod.
- matja 5y agojo -- -s a=123 -s b=00123 -s c="$(date)" {"a":"123","b":"00123","c":"Sat 5 Feb 21:29:08 GMT 2022"} What's the issue?
- pydry 5y agoThe problem is avoiding your surprise ruined Sunday night three months from now is predicated on you not forgetting to put the -s in. Implicit typing sucks: https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- nikolay 5y agoI've been using it for years and it's a great companion of jq and jp (JMESPath).
- EdSchouten 5y agoI remember that when I worked at Google about a decade ago, there was this common saying: "If the first version of your shell script is more than five lines long, you should have written it in Python." 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. For example, take this example: jo -p name=JP object=$(jo fruit=Orange point=$(jo x=10 y=20) number=17) sunday=false In Python you would write it like this: json.dumps({"name": "JP", "object": {"fruit": "Orange", "point": {"x": 10, "y": 20}, "number": 17}, "sunday": False}) 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.
- synergy20 5y agothat's probably you're at google, however there are more embedded devices on earth than whatever google has times 1+ billion. in those devices python is too heavy, and a posix shell along with jo fits perfectly.
- zimpenfish 5y ago> Even though Python isn't the fastest language out there, it's likely still faster than the shell command above. Taking these two command lines: jo -p name=JP object=$(jo fruit=Orange point=$(jo x=10 y=20) number=17) sunday=false >/dev/null python -c 'import json;print(json.dumps({"name": "JP", "object": {"fruit": "Orange", "point": {"x": 10, "y": 20}, "number": 17}, "sunday": False}))' >/dev/null For jo (x86_64, Rosetta2), python2 (x86_64, Rosetta2), jo (arm64), and python3 (arm64), running 1000 iterations, with `tai64n` doing the timing. 2022-02-05 21:25:38.357228500 start-jo-x86 2022-02-05 21:25:45.319337500 stop-jo 2022-02-05 21:25:45.319338500 start-python2-x86 2022-02-05 21:26:18.876235500 stop-python2-x86 2022-02-05 21:26:18.876235500 start-jo-arm 2022-02-05 21:26:22.316063500 stop-jo-arm 2022-02-05 21:26:22.316064500 start-python3-arm 2022-02-05 21:26:40.379063500 stop-python3-arm I make it: 7s for jo-x86, 33.5s for python2-x86, 3.5s for jo-arm, 18s for python3-arm. Test script is at https://pastebin.com/4tTVrDia https://pastebin.com/4tTVrDia
- kazinator 5y agoFive minute job: $ ./jo foo=1 bar=2 obj=$(./jo -a 1 2 3 "</script" '"') {"foo":1,"obj":[1,2,3,"<\/script","\""],"bar":2} $ ./jo foo='abc > def > ghi' {"foo":"abc\ndef\nghi"} $ cat jo #!/usr/local/bin/txr --lisp (define-option-struct jo-opts nil (a array :bool "Produce array instead of object") (nil help :bool "Print this help")) (defvarl jo-name *load-path*) (defun json-val (str) (match-case str ("true" t) ("false" nil) ("null" 'null) (`{@nil` (get-json str)) (`[@nil` (get-json str)) (@else (iflet ((num (tofloat else))) num else)))) (let ((o (new jo-opts))) o.(getopts *args*) (when o.help (put-line "Usage:\n") (put-line ` @{jo-name} [options] arg*`) o.(opthelp) (exit 0)) (if o.array (let ((items [mapcar json-val o.out-args])) (put-jsonl (vec-list items))) (let ((pairs [mapcar (lambda (:match) ((`@this=@that`) (list (json-val this) (json-val that))) ((@else) (error "~a: arguments must be name=obj pairs" jo-name))) o.out-args])) (put-jsonl ^#H(() ,*pairs)))))
- MathMonkeyMan 5y agoNever heard of txr. Thanks for this. For anyone else wondering: https://www.nongnu.org/txr/ https://www.nongnu.org/txr/
- ilyash 5y agoDepending 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. Disclosure: I'm the author Project link: https://github.com/ngs-lang/ngs https://github.com/ngs-lang/ngs Enjoy!
- hyperpallium2 5y agoThis looks like an informally specified shell-friendly alternative json syntax. 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.
- johncs 5y agoIn 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. $ alias fooson="node --eval \"console.log(JSON.stringify(eval('(' + process.argv[1] + ')')))\"" $ fooson "{time: $(date +%s), dir: '$HOME'}" {"time":1457195712,"dir":"/Users/jpm"} It may be a bit nicer to place that JavaScript in your path as a node script instead of using an alias. #!/usr/bin/env node console.log(JSON.stringify(eval('(' + process.argv[2] + ')'))) 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: #!/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] + ')'))) Now environmental variables can be access as if they were JS variables. This can let you handle strings with annoying quoting. $ export BAR="\"'''\"\"" $ fooson '{bar: BAR}' {"bar": "\"'''\"\""} 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. $ fooson "{'time': $(date +%s), 'dir': '$HOME'}" {"time":1457195712,"dir":"/Users/jpm"} If you taught the utility to expand env variables itself you'd be able to handle strings with mixed quoting as well. $ export BAR="\"'''\"\"" $ fooson '{"bar": "$BAR"}' {"bar": "\"'''\"\""} You'd only need small modifications to a JSON parser to make this work.
- johncs 5y agoEnded up sniping myself . Made a fairly complete version of what I was imagining: https://github.com/itsjohncs/construct-json#readme https://github.com/itsjohncs/construct-json#readme
- fooyc 5y ago> 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 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 `:`. This will be a source of bugs or security issues for any script generating json with dynamic values.