Skip to content
AgileLogics
6 min read

EDN and JSON

Prerequisites: Files and I/O

EDN — Clojure's data format

EDN (Extensible Data Notation) is Clojure's literal syntax used as a data format: maps, vectors, sets, keywords, strings, numbers — exactly what you write in code. Clojure projects use it for configuration (deps.edn) and data exchange.

(require '[clojure.edn :as edn])

(def config-text "{:port 8080 :hosts [\"a.local\" \"b.local\"] :debug? true :started #inst \"2026-09-29T08:00:00.000-00:00\"}")

(def config (edn/read-string config-text))
(println (:port config) (count (:hosts config)) (:debug? config))
(println (type (:started config)))

(def data {:user/id 7 :tags #{:admin} :scores [1 2 3]})
(def text (pr-str data))
(println text)
(println (= data (edn/read-string text)))
8080 2 true
java.util.Date
{:user/id 7, :tags #{:admin}, :scores [1 2 3]}
true
  • pr-str writes data as EDN; clojure.edn/read-string reads it back.
  • Tagged literals like #inst (a date) and #uuid extend the format.
  • Use clojure.edn/read-string, not clojure.core/read-string, for data from outside: the core reader can evaluate code.

Pretty printing

(require '[clojure.pprint :refer [pprint print-table]])

(pprint {:service "api"
         :replicas 3
         :env {:DATABASE_URL "postgres://db/app" :LOG_LEVEL "info" :FEATURE_FLAGS "a,b,c"}
         :ports [8080 9090]})

(print-table [{:name "Ada" :age 36} {:name "Linus" :age 28}])
{:service "api",
 :replicas 3,
 :env
 {:DATABASE_URL "postgres://db/app",
  :LOG_LEVEL "info",
  :FEATURE_FLAGS "a,b,c"},
 :ports [8080 9090]}

| :name | :age |
|-------+------|
|   Ada |   36 |
| Linus |   28 |

JSON with clojure.data.json

JSON needs a library. org.clojure/data.json is the official one (and cheshire and jsonista are popular faster alternatives). With the CLI, add it to deps.edn: {:deps {org.clojure/data.json {:mvn/version "2.5.2"}}}.

(require '[clojure.data.json :as json])

(def text "{\"name\":\"tools-api\",\"version\":\"2.1.0\",\"maintainers\":[{\"name\":\"Ada\",\"active\":true}],\"limits\":{\"rps\":50}}")

(def parsed (json/read-str text :key-fn keyword))
(println (:name parsed))
(println (map :name (:maintainers parsed)))
(println (get-in parsed [:limits :rps]))

(println (json/write-str {:id 7 :tags ["clojure" "json"] :owner {:name "Ada"} :note nil}))
(json/pprint {:id 7 :tags ["a"]})
tools-api
(Ada)
50
{"id":7,"tags":["clojure","json"],"owner":{"name":"Ada"},"note":null}
{"id":7, "tags":["a"]}
  • :key-fn keyword turns JSON object keys into keywords, so the result looks like ordinary Clojure data.
  • write-str converts keywords to strings, nil to null.

Inspect JSON with the JSON Formatter and JSON Validator.