FSharp.Data.JsonValidation


Introducing FSharp.Data.JsonValidation

A nifty little DSL for validating that JSON matches an expected schema.

First things first...

Let's include the library (use #r if you're writing an fsx), also include FSharp.Data

1: 
2: 
3: 
4: 
#r "FSharp.Data.JsonValidation.dll"

open FSharp.Data
open JsonValidation

Define your schemas

Next define some schemas that describe how the JSON you're expecting should look. Schemas are just values and can easily be combined.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let someNumbers = ArrayWhose [ItemsMatch AnyNumber]

let person =
  ObjectWhere
    [
      "name" .= StringThat [IsNotEmpty]
      "favoriteNumbers" .= someNumbers
      "email" .?= StringThat [MeetsCriteria ("be an email", looksLikeAnEmail)]
    ]

where looksLikeAnEmail is a silly little helper for illustrative purposes (i.e. you probably don't want to use this in production code) that looks like

1: 
2: 
let looksLikeAnEmail (value: string) =
  value.Contains "@" && value.Contains "." && value.Length > 3

Validate some JSON!

Lastly let's try some actual JSON!

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
let okayPerson =
  """
  {
    "name": "Saul Goodman",
    "favoriteNumbers": [747, 737000]
  }
  """

validate person (JsonValue.Parse okayPerson)
// returns: Valid

let emptyNamePerson =
  """
  {
    "name": "",
    "favoriteNumbers": [],
    "email": "mysterious@haunted.house"
  }
  """

validate person (JsonValue.Parse emptyNamePerson)
// returns: Invalid 


let badEmail =
  """
  {
    "name": "Jimmy M.",
    "favoriteNumbers": [],
    "email": "not-really-an-email"
  }
  """

validate person (JsonValue.Parse badEmail)
// returns: Invalid ".email Expected string \"not-really-an-email\" to be an email\""

What's going on here?

We're defining two different schemas

1: 
2: 
3: 
let someNumbers = ...

let person = ...

someNumbers is a simple schema that describes a JSON array whose items are any number. It will only be valid if, well, it's exactly that: an array containing number (or perhaps none).

person is a slightly more complicated schema. It says that a person must be a JSON object that must have a "name" key whose value is a non-empty string. ( .= ) indicates a required key whose value matches another schema.

The person object must also have "favoriteNumbers" which can be someNumbers (here we're re-using our other schema).

Finally, we're saying that person may have an "email" key with a value that meets some criteria. ( .?= ) indicates an optional key and MeetsCriteria takes a description of the expectation and an actual function (a predicate) to check whether it matches.

Fork me on GitHub