I need to validate the shape of clojure maps that have been converted from json strings. The json strings are messages of a protocol I'm implementing. For this I'm trying out clojure.spec.alpha.
I'm using s/keys. Multiple messages in my protocol have the same key names, but differently shaped values attached to those keys, so they cannot be validated by the same spec.
An example:
;; Here status should have the same key name, but their shape
;; is different. But definining another spec forces me to register it with a
;; different keyword, which breaks the "should have name 'status'" requirement.
(s/def ::a-message
(s/keys :req [::status]))
(s/def ::another-message
(s/keys :req [::status]))
I think I could define the :status spec in different namespaces, but it seems overkill to me. After all it's just different messages in the same protocol and i just have a couple of clashes.
Is there a way for (s/keys) to separate the name of the key whose presence is being checked from the name of the spec that is validating it?
In spec, qualified keywords are used to create global semantics (via the spec) whose name is the qualified keyword. If you use the same qualified keyword with different semantics, I'd say you should change your code to use different qualifiers :ex1/status and :ex2/status for different semantics.
If you are using unqualified keywords (not uncommon when coming from JSON), you can use s/keys
and :req-un
to map different specs to the same unqualified keyword in different parts of your data.
(s/def :ex1/status string?)
(s/def :ex2/status int?)
(s/def ::a-message (s/keys :req-un [:ex1/status]))
(s/def ::another-message (s/keys :req-un [:ex2/status]))
(s/valid? ::a-message {:status "abc"}) ;; true
(s/valid? ::another-message {:status 100}) ;; true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With