Clojure 规范映射

Clojure spec maps

有以下两个规格:

(s/def ::x keyword?)
(s/def ::y keyword?)
(s/def ::z keyword?)

(s/def ::a
  (s/keys :req-un [::x
                   ::y]
          :opt-un [::z]))

(s/def ::b
  (s/map-of string? string?))

如何将 ::a::b 组合成 ::m 以便以下数据有效:

(s/valid? ::m
           {:x :foo
            :y :bar
            :z :any})

(s/valid? ::m
          {:x :foo
           :y :bar})

(s/valid? ::m
          {:x :foo
           :y :bar
           :z :baz})

(s/valid? ::m
          {:x :foo
           :y :bar
           :z "baz"})

(s/valid? ::m
          {:x :foo
           :y :bar
           :t "tic"})

此外,如何将 ::a::b 组合成 ::m 以便以下数据无效:

(s/valid? ::m
          {"r" "foo"
           "t" "bar"})

(s/valid? ::m
          {:x :foo
           "r" "bar"})

(s/valid? ::m
           {:x :foo
            :y :bar
            :r :any})

两者都不是:


(s/def ::m (s/merge ::a ::b))

(s/def ::m (s/or :a ::a :b ::b))

有效(如预期),但有没有办法按照规范顺序的优先级匹配映射条目?

它的工作方式如下:

  1. 获取值的所有映射条目(这是一个映射)
  2. 将映射条目分成两组。一个确认 ::a 规范,另一个确认 ::b 规范。
  3. 两个子图整体上要符合相关规范。例如,第一个分区应具有所有必需的键。

您可以通过不将地图视为地图而是将其视为地图条目的集合来执行此操作,然后验证地图条目。必须通过 s/and 附加谓词来处理“必需”键部分。

(s/def ::x keyword?)
(s/def ::y keyword?)
(s/def ::z keyword?)

(s/def ::entry (s/or :x (s/tuple #{::x} ::x)
                     :y (s/tuple #{::y} ::y)
                     :z (s/tuple #{::z} ::z)
                     :str (s/tuple string? string?)))

(defn req-keys? [m] (and (contains? m :x) (contains? m :y)))

(s/def ::m (s/and map? (s/coll-of ::entry :into {}) req-keys?))