This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-10
Channels
- # aws (39)
- # babashka (4)
- # beginners (5)
- # biff (25)
- # cider (14)
- # clj-on-windows (40)
- # clojure-europe (36)
- # clojure-gamedev (1)
- # clojure-losangeles (4)
- # clojure-norway (51)
- # clojure-spec (5)
- # clojure-uk (2)
- # clojurescript (2)
- # clr (176)
- # data-science (10)
- # datalevin (17)
- # datomic (7)
- # deps-new (4)
- # docs (3)
- # emacs (12)
- # figwheel (3)
- # figwheel-main (5)
- # hyperfiddle (20)
- # instaparse (3)
- # introduce-yourself (8)
- # lsp (66)
- # malli (43)
- # off-topic (4)
- # rdf (11)
- # reagent (5)
- # releases (2)
- # sci (11)
- # shadow-cljs (24)
- # slack-help (2)
- # specter (7)
- # tools-deps (3)
- # xtdb (48)
Would it make sense for a :map
schema w/ keyword keys like [:map [:foo :string] [:bar :string]]
decoding w/ the json-transformer when it encounters a map like {"foo" "whatever" "bar" "thingy"}
to just turn those keys into keywords similar to what e.g. [:map-of :keyword :string]
would do? It doesn't seem to do this today, but I'm wondering if a PR to enable it to would be welcome (or to find out that I'm doing it wrong).
Good question: I would encourage using a key-transformer with the json-transformer. Something like
#_:clj-kondo/ignore ;;nocommit
(require '[malli.core :as mc] '[malli.transform :as mtx] '[cheshire.core :as json])
(mc/decode [:map [:k :string]]
(json/decode "{\"k\": \"v\"}")
(mtx/transformer
(mtx/json-transformer)
(mtx/key-transformer {:decode keyword})))
;; => {:k "v"}
well, I can't b/c I don't necessarily want all keys transformed to keywords. just the ones that are spec'd as keywords in the schema

[:foo :string]
is speccing foo
as a keyword
(mc/validate
(mc/schema [:map [:foo :string]])
{"foo" "a"})
;; => false
(mc/validate
(mc/schema [:map ['foo :string]])
{'foo "a"})
;; => true
True, however in my example above if I add a schema with a string as the key, it would still keywordize it
oops, you're correct, i misunderstood
yeah these schemas are for an internal representation that often comes in via an http api as json and we're using malli to coerce those to the internal representation
but some keys need to stay strings, so the key-transformer is a bit all-or-nothing
whereas the map schema already implicitly defines those types
that's what I'm doing currently via an additional :map-of
schema in an :and
w/ the :map
but it's kind of clunky
hence my question
looking at the source, seems the issue is that -json-decoders
doesn't do anything special for maps
various other types do transformations: string to keyword, etc
(defn m->m-decode-kws [map-schema]
(let [kws (set (map name (filter keyword? (mut/keys map-schema))))
kw-kws-fn (fn [m] (into {} (for [[k v] m] [(if (kws k) (keyword k) k) v])))]
(mut/update-properties map-schema assoc :decode/mine kw-kws-fn)))
(mc/decode (m->m-decode-kws [:map [:k :int] ["j" :int]])
{"k" 1, "j" 2}
(mtx/transformer {:name "mine"}))
;; => {:k 1, "j" 2}
and json-transformer only accepts map-of
special stuff
it records which keys are keywords in kws
, then calls keyword on them from the map’s custom transformer.
Yeah I agree
And now I realize you could make the exact same case for other literal value schemas like :enum
or :=
but that's not an argument against 🙂
fwiw, enum does decode its own keys:
(m/decode [:enum :k] "k" mt/json-transformer)
;;=> :k
Oh! Interesting!
Hi all, I’m new to malli but have some experience with spec tools - I’m committed to the switch from spec-tools. I’m struggling with the documentation around immutable registries -here’s my latest puzzle:
(malli/decoder [:map {:registry malli/default-registry}] malli.transform/json-transformer)
That blows up with a cryptic error message deep in the bowels of malli. Remove the reference to the default registry and it works as expected. Likewise, providing the registry as an option to decoder instead of inline in the spec works. I confess to being surprised by the malli registry and schema construction operations. An immutable registry is a big chunk of the value prop (along with transforms) of malli but I can’t master simple things like supplying a registry to transforms.try [:schema {:registry default-registry} [:map ,,,]]
(m/decoder [:schema {:registry m/default-registry} [:map]] mt/json-transformer)
gives me the same error.
could you add the error?
gats> (m/decoder [:schema {:registry m/default-registry} [:map]] mt/json-transformer)
Execution error (IllegalArgumentException) at malli.core/-property-registry (core.cljc:257).
Don't know how to create ISeq from: malli.registry$custom_default_registry$reify__28151
gats>
I remain stumped and I’ll raise an issue for this. There seems to be a lot of undocumented behavior, perhaps even bugs, around supplying immutable registries when transforming.
Will this work for you?
(m/decoder :map {:registry m/default-registry} (mt/json-transformer))
Yes, that does work. But it’s strange that it’s possible to associate a registry with a map schema (clearly required for recursive structures) but not have it be generally supported. Or documented as such. In any case, I can change my approach to not associate registries with maps even with the map entries are spec’d in the schema. Instead, I’ll provide the registry everywhere else (decode, decoder, encode, encoder, explain …)