prompts/skills/clojure-babashka-json/SKILL.md
babashka.json is a cross-platform JSON abstraction for Clojure/babashka. Use when working with JSON parsing, serialization, or writing portable code that runs on both JVM and babashka.
npx skillsauth add ramblurr/nix-devenv clojure-babashka-jsonInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A minimal abstraction over multiple JSON implementations, providing a unified API that works seamlessly in both JVM Clojure and babashka.
The library automatically selects the best available JSON implementation on your classpath without requiring conditional reader macros or platform-specific code.
deps.edn:
org.babashka/json {:mvn/version "0.1.7"}
Leiningen:
[org.babashka/json "0.1.7"]
See https://clojars.org/org.babashka/json for the latest version.
(require '[babashka.json :as json])
;; Parse JSON string to Clojure data
(json/read-str "{\"name\": \"Alice\", \"age\": 30}")
;; => {:name "Alice", :age 30}
;; Serialize Clojure data to JSON
(json/write-str {:name "Bob" :age 25})
;; => "{\"name\":\"Bob\",\"age\":25}"
;; Round-trip
(-> {:users [{:id 1} {:id 2}]}
json/write-str
json/read-str)
;; => {:users [{:id 1} {:id 2}]}
;; Basic parsing - keys become keywords by default
(json/read-str "{\"a\": 1, \"b\": 2}")
;; => {:a 1, :b 2}
;; Arrays
(json/read-str "[1, 2, 3]")
;; => [1 2 3]
;; Keep string keys
(json/read-str "{\"a\": 1}" {:key-fn str})
;; => {"a" 1}
;; Custom key transformation
(json/read-str "{\"user_id\": 123}" {:key-fn #(-> % keyword str/upper-case keyword)})
;; => {:USER_ID 123}
Options:
:key-fn - Function to transform JSON object keys. Defaults to keyword.;; Maps
(json/write-str {:name "Alice" :active true})
;; => "{\"name\":\"Alice\",\"active\":true}"
;; Vectors
(json/write-str [1 2 3])
;; => "[1,2,3]"
;; Nested structures
(json/write-str {:users [{:id 1 :name "Alice"}
{:id 2 :name "Bob"}]})
;; => "{\"users\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]}"
;; Keywords become strings
(json/write-str {:status :active})
;; => "{\"status\":\"active\"}"
(require '[clojure.java.io :as io])
;; Read from file
(with-open [rdr (io/reader "data.json")]
(json/read (json/->json-reader rdr)))
;; Read from string reader
(let [rdr (json/->json-reader (java.io.StringReader. "{\"a\": 1}"))]
(json/read rdr))
;; => {:a 1}
;; With custom key function
(let [rdr (json/->json-reader (java.io.StringReader. "{\"a\": 1}")
{:key-fn str})]
(json/read rdr {:key-fn str}))
;; => {"a" 1}
(json/get-provider)
;; => cheshire/cheshire (in babashka)
;; => org.clojure/data.json (on JVM without other deps)
On the JVM, babashka.json automatically uses the first available library in this order:
Force a specific provider via JVM property BEFORE loading the library:
;; In deps.edn :jvm-opts
:jvm-opts ["-Dbabashka.json.provider=com.cnuernber/charred"]
;; Or programmatically (must be before requiring babashka.json)
(System/setProperty "babashka.json.provider" "metosin/jsonista")
(require '[babashka.json :as json])
Valid provider values:
cheshire/cheshirecom.cnuernber/charredmetosin/jsonistaorg.clojure/data.json(require '[babashka.json :as json])
(defn fetch-user [id]
(-> (http/get (str "https://api.example.com/users/" id))
:body
(json/read-str)))
;; Use the data
(let [user (fetch-user 123)]
(println "User:" (:name user)))
(require '[clojure.java.io :as io]
'[babashka.json :as json])
;; Write JSON to file
(defn save-config [config path]
(spit path (json/write-str config)))
;; Read JSON from file
(defn load-config [path]
(json/read-str (slurp path)))
;; Streaming large files
(defn read-large-json [path]
(with-open [rdr (io/reader path)]
(json/read (json/->json-reader rdr))))
#!/usr/bin/env bb
(require '[babashka.json :as json])
;; Read from stdin
(let [data (json/read-str (slurp *in*))]
(println "Processing" (count data) "records"))
;; Write to stdout
(println (json/write-str {:status "ok" :timestamp (System/currentTimeMillis)}))
Instead of this:
#?(:bb (cheshire.core/parse-string s keyword)
:clj (clojure.data.json/read-str s :key-fn keyword))
Write this:
(require '[babashka.json :as json])
(json/read-str s)
Works identically on both platforms.
Keywords vs Strings: By default, JSON object keys become keywords. Use :key-fn str if you need string keys.
Provider must be set early: The babashka.json.provider system property must be set BEFORE the library is loaded. Setting it after has no effect.
Excluding the bundled dependency: If you don't want org.clojure/data.json on your classpath:
org.babashka/json {:mvn/version "0.1.7"
:exclusions [org.clojure/data.json]}
But ensure you have another JSON library available.
Minimal API surface: This library intentionally provides only the most common operations. For advanced features (pretty printing, custom encoders, streaming), use the underlying provider directly.
Different providers have different behavior: While the API is unified, edge cases (number precision, date handling) may differ between providers. Test your specific use case if switching providers.
Reader options must match: When using ->json-reader with options, pass the same options to read:
;; Correct
(let [rdr (json/->json-reader input {:key-fn str})]
(json/read rdr {:key-fn str}))
;; May not work as expected
(let [rdr (json/->json-reader input {:key-fn str})]
(json/read rdr)) ; Missing {:key-fn str}
Use babashka.json when:
Don't use babashka.json when:
For advanced use cases, depend on your chosen provider directly (cheshire, jsonista, etc.) and use its full API.
testing
Use this OCP when executing or preparing to execute commands that change a live or important system, service reloads/restarts, package changes, deployments, migrations, firewall/network/access changes, credential rotation, NixOS switch/test/boot/deploy, or incident mitigation. It guides safe operations with a persisted ledger for scope, preflight, baseline, rollback, validation, and evidence.
development
Create new agent skills with proper structure, progressive disclosure, and bundled resources. Use when user wants to create, write, or build a new skill.
documentation
Naming conventions for workflow documents in prompts/. Use when creating plans, PRDs, research reports, idea capture or other workflow documents. Triggers on (1) creating new planning documents, (2) naming PRDs or research reports, (3) questions about document organization in prompts/.
testing
Grilling session that challenges your plan against the existing domain model, sharpens terminology, and updates documentation (CONTEXT.md, ADRs) inline as decisions crystallise. Use when user wants to stress-test a plan against their project's language and documented decisions.