Skip to content

Instantly share code, notes, and snippets.

@Profpatsch
Last active October 27, 2021 09:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Profpatsch/af40fc52f51cc94796e05bf7fed395ed to your computer and use it in GitHub Desktop.
Save Profpatsch/af40fc52f51cc94796e05bf7fed395ed to your computer and use it in GitHub Desktop.
Encoding sum types with json-schema

The goal is to express an ADT like the following in json schema:

data Sum 
  = Foo { hi :: Int }
  | Bar { x :: Bool }

or in rust syntax:

enum Sum {
  Foo { hi : i64 },
  Bar { x : bool }
}

This can be done by encoding each of these alternatives in a “tagged” way in json:

{
  "tag": "Foo",
  "hi": 234
}

or

{ 
  "tag": "Bar",
  "x": true
}

Of course this means the field tag cannot be used for any of the field names, but it’s a good encoding for most things.

Now, how to express this in json-schema? oneOf to the rescue.

{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"oneOf": [
{ "type": "object",
"required": [ "tag", "hi" ],
"properties": {
"tag": {
"const": "foo"
},
"hi": {
"type": "integer"
}
}
},
{ "type": "object",
"required": [ "tag", "x" ],
"properties": {
"tag": {
"const": "bar"
},
"x": {
"type": "boolean"
}
}
}
]
}

This will use the const type to differentiate the tag field for the record options. It’s slightly verbose, but should work with any standard-compliant parsers. Don’t forget to add "tag" to required in every branch, otherwise you get weird matches/a weird machine.

If your parser doesn’t yet support the draft 6 of json-schema, you can use "enum": [ "tag" ] instead of "const": "tag".

screenshot screenshot screenshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment