I'm trying to implement a custom transformer for a schema in my registry but cannot get it to work. I have
(ns test
(:require
[malli.core :as m]
[malli.registry :as mr]
[malli.transform :as mt]))
(defrecord Foo [x])
(def foo? (partial instance? Foo))
(mr/set-default-registry!
(mr/composite-registry
(m/default-schemas)
(mr/simple-registry {:foo [:fn foo?]})))
(def string->foo-transformer (mt/transformer {:name :string-foo-transformer
:encoders {:foo (fn [x] (->Foo x))
[:fn (partial instance? Foo)]
(fn [x] (->Foo x))
'foo?
(fn [x] (->Foo x))}
:decoders {:foo (fn [x] (->Foo x))
[:fn (partial instance? Foo)]
(fn [x] (->Foo x))
'foo?
(fn [x] (->Foo x))}}))
(m/decode [:map [:x :foo]] {:x 1} string->foo-transformer)
;; => {:x 1}
(m/decode :foo 1 string->foo-transformer)
;; => 1
I would expect to get something like
;; => {:x #test.Foo{:x 1}}
;; => #test.Foo{:x 1}
Any pointers what I'm doing wrong?I'm not familiar with passing the :encoders and :decoders when creating a named transformer but I might just be ignorant about that. I usually do something like this:
(def string->foo-transformer (mt/transformer {:name :string-foo-transformer}))
(m/decode [:map
[:x {:decode/string-foo-transformer ->Foo} :foo]]
{:x 1}
string->foo-transformer)It looks like the decoders test is applied on the top level of the schema, e.g. this works:
(def string->foo-transformer (mt/transformer {:name :string-foo-transformer
:decoders {:map ->Foo}}))
(m/decode [:map [:x :foo]]
{:x 1}
string->foo-transformer)Thank you! I think the issue is that the lookup does not respect the schemas from the custom registry:
(m/decode :string "foo" (mt/transformer {:name :string->foo-transformer
:decoders {:string ->Foo}}))
;; => #test.Foo{:x "foo"}
(m/decode :foo "foo" (mt/transformer {:name :string->foo-transformer
:decoders {:foo ->Foo}}))
;; => "foo"e.g. it works with the malli built-in :string but it does not work with my custom :foo schema
The dispatch is on m/type so this works:
(m/decode :foo "foo" (mt/transformer {:name :string->foo-transformer
:decoders {:fn ->Foo}}))
;; => #test.Foo{:x "foo"}
But it's not really what I was expecting.Ah I guess that makes sense if you think of :foo as the schema name or key and :fn as the type of the schema
Yes. But I would like to be able to use different transformers for different schemas. I don't think that's possible like this, is it?
It looks like you need to set the type of the custom schema:
(mr/set-default-registry!
(mr/composite-registry
(m/default-schemas)
{:foo (m/-simple-schema {:type :foo
:pred foo?})}))Ah, thanks!