I'm trying to parse a json message from aws cli but got stuck pretty early - what am I doing wrong?
echo '{"DecodedMessage" : "{\"allowed\":false,\"explicitDeny\":false}"}' | bb -i "(do (require '[cheshire.core :as json]) (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))" | bb -i "(do (require '[cheshire.core :as json])
(let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))"
----- Error --------------------------------------------------------------------
Type: java.lang.ClassCastException
Message: clojure.lang.Cons cannot be cast to java.lang.String
Location: <expr>:2:50
----- Context ------------------------------------------------------------------
1: (do (require '[cheshire.core :as json])
2: (let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))
^--- clojure.lang.Cons cannot be cast to java.lang.String
----- Stack trace --------------------------------------------------------------
cheshire.core/parse-string - <built-in>
user - <expr>:2:50
In the REPL, this works:
(def *input* "{\"DecodedMessage\" : \"{\\\"allowed\\\":false,\\\"explicitDeny\\\":false}\"}")
(do (require '[cheshire.core :as json])
(let [{:strs [DecodedMessage] :as msg} (json/parse-string *input*)] (println DecodedMessage) ))
;;=>
{"allowed":false,"explicitDeny":false}
I think
-i Bind *input* to a lazy seq of lines from stdi
@jumar Can you try replacing -i
with -e
?Ah right, now I'm getting a different error π
----- Error --------------------------------------------------------------------
Type: clojure.lang.EdnReader$ReaderException
Message: java.lang.RuntimeException: Invalid token: :
Location: 1:99
----- Stack trace --------------------------------------------------------------
- <expr>:1:99
cheshire.core/parse-string - <built-in>
user - <expr>:1:80
Btw. is this how would you write such a simple thing? Is that require
even "required"?
You can just use cheshire for this with parse-stream and *in*
When using -i
then input becomes a lazy seq of lines. You should be aware of this. Cheshire cannot parse a lazy seq of text, it just parses text.
Well, I replaced -i
with -e
as per your suggestion.
I'll try parse-stream
That works!
echo '{"DecodedMessage" : "{\"allowed\":false,\"explicitDeny\":false}"}' | bb -e "(do (require '[cheshire.core :as json]) (let [{:strs [DecodedMessage] :as msg} (json/parse-stream *in*)] (println DecodedMessage) ))"
{"allowed":false,"explicitDeny":false}
@borkdude I think forgetting -i
or using *input*
instead of *in*
are easy mistakes to make. Did you consider using something like delays to gives some user feedback here? E.g. @`*input*` could throw an error if -i
is not used. Maybe it would also be helpful if there was one var for a particular input. Now *input*
can have different shapes based on the flag. I understand something like this would be a breaking change, but Iβm curious what you think
You don't need to use -i
and *input*
together, if you don't provide -i
input contains EDN from stdin
Just read the docs, it's explained pretty elaborately.
And it's optional to use, you can also just use *in*
and write more verbose code.
Yes I understand that reading the docs would help. I was thinking there might be a more foolproof way. But your position is clear :)
These flags were invented when babashka was more or less just for one-liners and it could not run scripts, namespaces, macros, etc, etc. I don't use these flags a lot myself. Maybe I should hide them a little bit in the README ;)
maybe hide them behind a flag --show-flags
π
I was also thinking we could have another flag -j
for JSON ;)
or maybe --input json
--input lines
, --input edn
, --input edn-stream
, --input json
, --input json-stream
It sounds useful. I havenβt done much with input yet. The potential error cases would grow Iβm guessing, maybe like the example above
maybe --input lines
and --input edn-stream
is more clear than -i and -I
it defaults to --input edn
(only one edn val from stdin)
also --output lines
and --output edn-stream
Or an idea could be to not have flags, but leave it to the program? Maybe something like https://github.com/originrose/lazy-map that you would call like (:edn-stream *input*)
. It would start parsing on the first call (i.e. lazy). If the value is incorrect it would throw. If there is no input it would throw. Would this make sense?
I think those would just be functions based on *in*
no special libs needed. can already write those
Thatβs true, only benefit would be that you donβt have to have a reference somewhere. But maybe itβs too magical for little benefit
The benefit of not having flags is that it works better for scripts.
http://babashka.io helper namespace with parse-edn-stream, etc could work
which then deprecates these flags over time
Makes sense. Would also make example scripts more explicit (no need to guess the flag settings)
(http://babashka.io/edn-stream) (assumes input is in) (http://babashka.io/edn-stream reader) (explicit reader) same for json, transit
and these functions could be part of the user namespace for one-liners
echo '{:a 1} {:a 2}' | bb '(edn-stream)'
echo '{:a 1} {:a 2}' | bb '(edn-stream *in*)'
for more explicitness
(maybe better to be explicit anyway)
Nice π And this lib could potentially be used from the jvm as well (I guess). So would make it easier to reuse both directions
yeah
@borkdude Thanks for listening π
:thumbsup: