Malli 高级使用技巧与实践指南

Malli 高级使用技巧与实践指南

Malli 是一个强大的 Clojure/Script 数据验证和转换库,提供了丰富的功能和灵活的扩展方式。本文将深入探讨 Malli 的一些高级使用技巧,帮助开发者更好地利用这个工具解决实际问题。

在转换中同时访问 Schema 和值

在实际开发中,我们经常需要在数据转换过程中同时访问 Schema 定义和实际值。Malli 提供了这种能力:

(m/decode
 Address
 lillan
 (mt/transformer
  {:default-decoder
   {:compile (fn [schema _]
               (fn [value]
                 (prn [value (m/form schema)])
                 value)}}))

这种技术特别适用于需要根据 Schema 定义对值进行特殊处理的场景,比如日志记录、调试或复杂的业务逻辑转换。

基于属性过滤 Schema

Malli 允许我们递归遍历 Schema 并根据属性进行过滤:

(m/walk
  Schema
  (fn [schema _ children options]
    (when-not (:deleteMe (m/properties schema))
      (let [children (if (m/entries schema) (filterv last children) children)]
        (try (m/into-schema (m/type schema) (m/properties schema) children options)
             (catch Exception _)))))

这个技巧可用于:

  • 根据环境过滤敏感字段
  • 动态调整 Schema 结构
  • 实现条件性验证规则

字符串处理技巧

Malli 提供了灵活的字符串处理能力,例如自动修剪字符串:

(defn string-trimmer []
  (mt/transformer
    {:decoders
     {:string
      {:compile (fn [schema _]
                  (let [{:string/keys [trim]} (m/properties schema)]
                    (when trim #(cond-> % (string? %) str/trim))))}}}))

(m/decode [:string {:string/trim true, :min 1}] " kikka  " string-trimmer)
;; => "kikka"

这种技术可以确保用户输入的数据符合格式要求,同时保持业务逻辑的简洁性。

集合类型解码

Malli 可以轻松处理集合类型的转换,例如将逗号分隔的字符串转换为整数向量:

(m/decode
  [:vector {:decode/string #(str/split % #",")} int?]
  "1,2,3,4"
  (mt/string-transformer))
;; => [1 2 3 4]

对于更复杂的需求,我们可以创建自定义转换器:

(defn query-decoder [schema]
  (m/decoder
    schema
    (mt/transformer
      (mt/transformer
        {:name "vectorize strings"
         :decoders
         {:vector
          {:compile (fn [schema _]
                      (let [separator (-> schema m/properties :query/separator (or ","))]
                        (fn [x]
                          (cond
                            (not (string? x)) x
                            (str/includes? x separator) (into [] (.split ^String x separator))
                            :else [x]))))}}})
      (mt/string-transformer))))

默认值的高级用法

Malli 不仅支持静态默认值,还可以通过函数动态计算默认值:

(defn default-fn-value-transformer
  ([]
   (default-fn-value-transformer nil))
  ([{:keys [key] :or {key :default-fn}}]
   (let [add-defaults
         {:compile
          (fn [schema _]
            (let [->k-default (fn [[k {default key :keys [optional]} v]]
                                (when-not optional
                                  (when-some [default (or default (some-> v m/properties key))]
                                    [k default])))
                  defaults    (into {} (keep ->k-default) (m/children schema))
                  exercise    (fn [x defaults]
                                (reduce-kv (fn [acc k v]
                                             (if-not (contains? x k)
                                               (-> (assoc acc k ((m/eval v) x))
                                               acc))
                                           x defaults))]
              (when (seq defaults)
                (fn [x] (if (map? x) (exercise x defaults) x)))))}]
     (mt/transformer
      {:decoders {:map add-defaults}
       :encoders {:map add-defaults}}))))

这种技术特别适用于:

  • 计算派生字段
  • 实现字段间的依赖关系
  • 提供上下文相关的默认值

Schema 转换与映射

Malli 提供了强大的 Schema 转换能力,可以递归地修改 Schema 结构:

(defn schema-mapper [m]
  (fn [s] ((or (get m (m/type s))
               (get m ::default)
               (constantly s))
           s)))

(m/walk
 [:map
  [:id :keyword]
  [:size :int]
  [:tags [:set :keyword]]]
 (m/schema-walker
  (schema-mapper
    {:keyword (constantly :string)
     :int #(m/-set-properties % {:gen/elements [1 2]})
     ::default #(m/-set-properties % {::type (m/type %)})})))

这种技术可用于:

  • 不同环境间的 Schema 转换
  • 版本兼容性处理
  • 生成测试数据

错误处理与人性化展示

Malli 提供了丰富的错误信息,可以将其转换为更友好的格式:

(-> [:map
     [:x :int]
     [:y [:set :keyword]]
    (m/explain {:x "1" :y #{:a "b"}})
    (me/humanize {:wrap #(select-keys % [:value :message])}))
;; => {:x [{:value "1" :message "should be an integer"}],
;;     :y #{[{:value "b" :message "should be a keyword"}]}}

这种技术可以显著提升用户体验,特别是在表单验证等场景中。

总结

Malli 提供了丰富而灵活的功能,通过本文介绍的高级技巧,开发者可以:

  1. 实现复杂的数据转换逻辑
  2. 动态调整 Schema 结构
  3. 处理各种边界条件和特殊需求
  4. 提供更好的用户体验

这些技术可以组合使用,解决实际开发中遇到的各种数据验证和转换挑战。Malli 的设计理念强调组合性和可扩展性,使得开发者可以根据具体需求定制解决方案。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余钧冰Daniel

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值