1

我有一个验证传入数据内容的规范定义。由于数据是字段映射,因此我spec/keys用于验证它。例如:

(def person-data {:name "Jon Doe", :age 30})
(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person-info (s/keys :req-un [::name ::age])
...
;validate data via spec and make sure no additional keys are included
(s/valid? ::person-spec some-input)

但我还有一个额外的需要是确保传入的数据只包含我想要的键。(在这种情况下:name:age只有键。为此,我做了类似的事情:

(def permitted-keys [:age :name])
(select-keys some-input permitted-keys)

,确保只有那些键被过滤。

有没有一种方法可以在我的映射结构规范定义(s/keys)和我为过滤允许的键(permitted-keys)而采取的这个额外步骤之间重用一些代码?

也许通过从s/keys定义中提取键列表,或者通过将现有的键向量传递给s/keys

4

1 回答 1

1

我建议查看这个宏,因为它涵盖了所有基础并提供了一个生成器,但这里有另一个假设您的地图使用未命名空间的键:

(defmacro keys-strict
  [& args]
  (let [{:keys [req opt req-un opt-un]} args
        ks (into #{} (->> (concat req opt req-un opt-un)
                          (map #(keyword (name %)))))] ;; strip namespaces from keywords
    `(s/and (s/keys ~@args) (s/map-of ~ks any?))))

此处为键重用相同事实来源的唯一技巧是您的键规范将被命名空间,但您的映射键不会。你可以在没有宏的情况下做同样的事情,你只是s/and你的s/keys规范与一个s/map-of规范或其他一些限制允许的键的规范。

有没有一种方法可以在我的映射结构(s/keys)的规范定义和我为过滤允许的键(permitted-keys)而采取的这个额外步骤之间重用一些代码?

是的,这在上面的例子中是通过拼接argss/keys调用上来处理的,并且在这个更完整的宏here中也是如此。

注意:在某些情况下,您可能确实需要限制您将接受的密钥,但我认为通常建议您定义对以后扩展开放的地图规范。

于 2017-12-12T21:26:56.783 回答