I'm using compojure to put together a RESTful service. I have a PUT route configured that takes some json as the request body and I've captured that data and have it represented as a map (using cheshire for the translation).
becomes clojure map
All lovely so far. Now here's the question:
What is the easiest way to perform some validation on the structure of the map? In this example I would want to verify that the map contains the keyword :foo. I'm not too concerned if there's extra other keywords in the map but I would want to know if an expected keyword is not present, say if the service user misspelled "foo" and :fooo was received instead of :foo.
I know I could achieve this with a series of accessor functions but aside from it being a bit tedious I would be easily tripped up by a valid key whose value is nil as you also get a nil when asking for a key that doesn't exist. For example (using a repl)
Asking for a valid key that has a value nil and asking for a non present key look the same.
Any suggestions on how I might achieve this neatly?
Having spent the afternoon digging around on this I have arrived at a solution. As I suspected I am not the first person in the world to face this issue and have found a lovely little library called Schema. As the name alludes to, Schema allows you to define a schema for arbitrary data structures including maps. Let's look at my example again. I want to verify that my map contains the key :foo. I can create a schema that describes a single item map with key :foo and value of type String:
Now I can use that data structure (schema) to validate any map
I was going to suggest Schema - it's an excellent run time validation library.
In the language itself, you might want to look :pre and :post condition checking for functions (argument and result value assertions).
Another interesting option is core.typed but that's more for (static) type checking of your code's consistency, not for validating that user input matches a particular structure. It you could declare a ValidatedData type and annotate your code, post-validation, with that and core.typed could verify that your code is "safe" in terms of consistent usage of the data structures (e.g., if you declare that :foo is :mandatory in a ValidatedData object, then doing (:foo valid-data) is never going to return nil so your code wouldn't need to check for it in order to be "type safe").
We started using Schema at World Singles, but you need good tests to exercise all of the run time checks. We switched to core.typed but it's hard work since you must satisfy a full static type checker and some Clojure idioms (such as nil-punning) can make that very hard to work with.
I spent the morning putting in a comma and the afternoon removing it.
-- Gustave Flaubert, French realist novelist (1821-1880)
Uh oh, we're definitely being carded. Here, show him this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop