clojure-spec

Franco Gasperino 2021-09-07T22:27:36.054600Z

I have read that s/keys only supports keywords. I have a large external json message for which i would to perform some top-level schema validation. Rather than performing a keywordize-keys, which may descend past the desired schema and into embedded content, I was hoping to keep the message keys as strings and use validation upon them. From what i can see, this would be done similar to this:

(def last-name? #(= "last" %))
  (def first-name? #(= "first" %))
  (def age? #(= "age" %))

  (spec/def ::last-name (spec/tuple last-name? string?))
  (spec/def ::first-name (spec/tuple first-name? string?))
  (spec/def ::age (spec/tuple age? (spec/and pos-int? int?)))

  (spec/def 
   ::who
   (spec/coll-of 
    (spec/or :last ::last-name :first ::first-name :age ::age) 
    :kind map?
    :count 3
    :distinct true))
  
  (spec/explain ::who {"last" "smith" "first" "bob" "age" 10})
Is this the recommended path when using map keys which are strings, not keywords?

Alex Miller (Clojure team) 2021-09-07T22:37:51.057300Z

This approach should work, you can simplify those preds using sets though, like #{“first”} (and at that point I would just inline them into ::first-name etc

Alex Miller (Clojure team) 2021-09-07T22:39:08.058600Z

You also might find it useful to wrap s/nonconforming around the s/or to simplify the conformed structure

Franco Gasperino 2021-09-07T22:51:44.059700Z

can you provide a case where s/conform who json-map would return something undesirable, where s/nonconforming would be wanted?

Alex Miller (Clojure team) 2021-09-07T23:08:58.060200Z

(spec/or :last ::last-name :first ::first-name :age ::age) is going to conform to [:last ["last" "smith"]]

Alex Miller (Clojure team) 2021-09-07T23:09:49.061Z

(spec/nonconforming (spec/or :last ::last-name :first ::first-name :age ::age)) will conform to ["last" "smith"]

Alex Miller (Clojure team) 2021-09-07T23:10:16.061600Z

if you use :into {} in your who spec, you can actually conform back to the original map if you want that

Franco Gasperino 2021-09-07T23:10:43.062Z

great to know. appreciated

Franco Gasperino 2021-09-07T23:19:43.062900Z

didn't need the :into. wrapping the s/or in a nonconforming was enough to return a map when calling conform