Who Am I

Who Am I?
- Hi I'm Ryan.
- I'm a community focused software Engineer currently working @ Bluetel (Huge thanks for allowing me to take the day to do this)
- I help Co-organise codebar Birmingham
- I have an unhealthy obsession with programming mascots π π (JSON schema mascot coming when?)
- Β

I have no idea what I am doing
I have no idea what I am doing

Me
All the other speakers... and most of the people in the room
An I am here to talk about....


(if you are my older brother)
For the uninitiated
{
"$schema": "http://json-schema.org/v1-coming-soon-πππ/schema#",
"title": "My Cool JSON Schema",
"type": "object",
"properties": {
"what": {
"type": "string"
},
"is": {
"type": "number"
},
"a": {
"enum": ["json", "schema"]
},
},
"additionalProperties": false,
"required": [
"version"
]
}For the uninitiated
{
"$schema": "http://json-schema.org/v1-coming-soon-πππ/schema#",
"title": "My Cool JSON Schema",
"type": "object",
"properties": {
"what": {
"type": "string"
},
"is": {
"type": "number"
},
"a": {
"enum": ["json", "schema"]
},
},
"additionalProperties": false,
"required": [
"version"
]
}{
"what": "foo",
"is": 5,
"a": "json"
}For the uninitiated
{
"$schema": "http://json-schema.org/v1-coming-soon-πππ/schema#",
"title": "My Cool JSON Schema",
"type": "object",
"properties": {
"what": {
"type": "string"
},
"is": {
"type": "number"
},
"a": {
"enum": ["json", "schema"]
},
},
"additionalProperties": false,
"required": [
"version"
]
}{
"what": "foo",
"is": 5,
"a": "Invalid"
}The Mission
Use this
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "My Cool JSON Schema",
"type": "object",
"properties": {
"what": {
"type": "string"
},
"is": {
"type": "number"
},
"a": {
"enum": ["json", "schema"]
},
},
"additionalProperties": false,
"required": [
"version"
]
}{
"what": "foo",
"is": 5,
"a": "json"
}To Generate this
Why?


{well}
{structured}
{data}
{yaaay}


{well}
{structured}
{data}
{yaaay}



:(


π



2 YEARS

{Go Chaff}
How bad could it be...
Lets start with an easy one!
Easy!
{
"type": "number"
}4.5Generate
Easy -ish
{
"type": "integer"
}5Generate
Easy?
{
"type": "integer",
"minimum": 10
}10Generate
easy*
{
"type": "integer",
"exclusiveMinimum": 10
}11Generate
α΄α΄sΚ
{
"type": "integer",
"multipleOf": 7,
"exclusiveMinimum": 10
}14Generate
:(
{
"type": "number",
"multipleOf": 0.00324634,
"exclusiveMinimum": 0.00634,
"maximum": 0.086785345
}π« Generate
Ok let's take a step back
maybe strings will be easier
{
"type": "string"
}Generate
"foo"{
"type": "string",
"format": "time"
}Generate
"???"(Annotation as of latest draft)



{
"type": "string",
"format": "time"
}Generate
"03:46:13+00:00"{
"type": "string",
"pattern": "\w+"
}oh no...
{
"type": "string",
"pattern": "\w+\d{0,1}"
}oh no... please no...
{
"type": "string",
"pattern": "^(?=.{1,256})(?=.{1,64}@.{1,255}$)(?:(?![_.-])[a-zA-Z0-9._%+-]{1,64}(?:(?:(?<!\\)[.,;:])?(?=.{0,64})[a-zA-Z0-9._%+-]{0,64})?@(?:(?!-)[a-zA-Z0-9-]{1,63}(?:(?:(?<!\\)[.,;:])?(?=.{0,255})[a-zA-Z0-9-]{0,63})?\.?)+(?:(?!-)[a-zA-Z]{2,})?|(?:[a-zA-Z0-9-]{1,63}\.[a-zA-Z]{2,}))(?=.{1,256})(?=.{1,64}@.{1,255}$)(?:(?![_.-])[a-zA-Z0-9._%+-]{1,64}(?:(?:(?<!\\)[.,;:])?(?=.{0,64})[a-zA-Z0-9._%+-]{0,64})?@(?:(?!-)[a-zA-Z0-9-]{1,63}(?:(?:(?<!\\)[.,;:])?(?=.{0,255})[a-zA-Z0-9-]{0,63})?\.?)+(?:(?!-)[a-zA-Z]{2,})?|(?:[a-zA-Z0-9-]{1,63}\.[a-zA-Z]{2,}))(?=.{1,256})(?=.{1,64}@.{1,255}$)(?:(?![_.-])[a-zA-Z0-9._%+-]{1,64}(?:(?:(?<!\\)[.,;:])?(?=.{0,64})[a-zA-Z0-9._%+-]{0,64})?@(?:(?!-)[a-zA-Z0-9-]{1,63}(?:(?:(?<!\\)[.,;:])?(?=.{0,255})[a-zA-Z0-9-]{0,63})?\.?)+(?:(?!-)[a-zA-Z]{2,})?|(?:[a-zA-Z0-9-]{1,63}\.[a-zA-Z]{2,}))(?=.{1,256})(?=.{1,64}@.{1,255}$)(?:(?![_.-])[a-zA-Z0-9._%+-]{1,64}(?:(?:(?<!\\)[.,;:])?(?=.{0,64})[a-zA-Z0-9._%+-]{0,64})?@(?:(?!-)[a-zA-Z0-9-]{1,63}(?:(?:(?<!\\)[.,;:])?(?=.{0,255})[a-zA-Z0-9-]{0,63})?\.?)+(?:(?!-)[a-zA-Z]{2,})?|(?:[a-zA-Z0-9-]{1,63}\.[a-zA-Z]{2,}))$"
}π


Json's first scheme
Scheme 1: Constraints easy one direction...
{
"type": "number",
"multipleOf": 0.5,
"minimum": 6
}5Is valid?
Scheme 1: Constraints easy one direction...
5{"type": "number"}{"multipleOf": 0.5}β
{"minimum": 6}β
β
β
Not Valid
Scheme 1: Constraints easy one direction... but not the other
???
{"type": "number"}{"multipleOf": 0.5}{"minimum": 6}???
Β―\_(γ)_/Β―Anything!
Err... -1?
Err... -0.5?
Err 7 right?
How can we go about fixing this?
Compiling "Generators" ahead of time
{
"type": "number",
"multipleOf": 0.5,
"minimum": 6
}Random number generator:
- Must Have number higher than 6
- Must only generate in increments of 0.5{
"type": "object",
"properties": {
"some_array": {
"type": "array",
"minItems": 5,
"items": {
"const": "test"
}
},
"some_string": {
"minLength": 5
}
}
}Then recursively building a tree of generatorsΒ
Object Generator
"some_array"
Array Generator(Min Len: 5)
Then recursively building a tree of generatorsΒ
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Scheme 1: Constraints easy one direction... but not the other
Scheme 1: Constraints easy one direction... but not the other

So we are done right? π
{
"allOf": [
{
"type": "string",
"minLength": 5
},
{
"format": "time"
}
]
}'allOf' the pain
scheme 2: Combination

scheme 2: Combination


scheme 2: Combination


π€
scheme 2: Combinators


π±
How can we work around this?
anyOf
{
"anyOf": [
{"type": "string"},
{"type": "number"}
]
}anyOf
{
"anyOf": [
{"type": "string"},
{"type": "number"}
]
}"yay"β
oneOf
{
"oneOf": [
{
"enum": ["foo", "baz"]
},
{
"enum": ["baz", "bar"]
}
]
}oneOf
{
"oneOf": [
{
"enum": ["foo", "baz"]
},
{
"enum": ["baz", "bar"]
}
]
}Generate
"baz"oneOf
{
"oneOf": [
{
"enum": ["foo", "baz"]
},
{
"enum": ["baz", "bar"]
}
]
}"baz"Validate
oneOf
{
"oneOf": [
{
"enum": ["foo", "baz"]
},
{
"enum": ["baz", "bar"]
}
]
}Generate
"foo"Repeat if not valid
(up to a limit)
allOf
{
"allOf": [
{
"minLength": "5"
},
{
"type": "string"
}
]
}allOf
"allOf": [
{
"minimumLength": "5"
},
{
"type": "string"
}
]
"type": "string",
"minimumLength": 5,
Merge
allOf
"allOf": [
{
"minimumLength": "5"
},
{
"type": "string"
}
]
"type": "string",
"minimumLength": 5,
Merge
Generate
"Heloooo"Easy right....?
allOf: Err what about this?
{
"allOf": [
{"type": "integer"},
{"multipleOf": 8},
{"multipleOf": 14},
{"minimum": 20},
{"exclusiveMinimum": 55},
{"maximum": 600}
]
}allOf: Custom Merge algorithm
{
"exclusiveMinimum" : 55,
"maximum" : 600,
"minimum" : 20,
"multipleOf" : 112,
"type" : "integer"
}Make the most restrictive intersection of all given nodes
scheme 2: Combinators
scheme 2: Combinators

Ok so we are done right... right?
{
"$ref": "https://despair.com/hahah.json#/nope"
}
Is that URL
IS THAT A URL
Scheme 3: $refusing to accept defeat
{
"$schema": "http://json-schema.org/draft-9001/schema#",
"type": "object",
"properties": {
"who_would_be_silly_enough_to_make_a_json_schem_faker": {
"$ref": "#/$defs/me"
}
},
"$defs": {
"me": {
"properties": {
"pain": {"const": "owwww"},
"suffering": {"const": "ouchie"}
},
"required": ["pain", "suffering"]
}
}
}Scheme 3: $refusing to accept defeat
| Reference | Generator |
|---|---|
| #/$defs/me/properties/suffering | ConstGenerator: "Oww" |
| #/$defs/me/properties/pain | ConstGenerator: "Suffering" |
| #/$defs/me | ObjectGenerator |
| #/properties/who_would_be_silly_enough_to_make_a_json_schem_faker | ReferenceGenerator: "#/$defs/me" |
| # | Root Generator |
Scheme 3: It's time to write a web crawlerπ·οΈ
{
"type": "object",
"properties": {
"who_would_be_silly_enough_to_make_a_json_schem_faker": {
"$ref": "http://www.example.com#/$defs/me"
}
}
}# http://www.example.com
{
"$defs": {
"me": {
"properties": {
"pain": {"const": "owwww"},
"suffering": {"const": "ouchie"}
}
"required": ["pain", "suffering"]
}
}
}Scheme 3: It's time to write a web crawlerπ·οΈ
| Reference | Generator |
|---|---|
| #/properties/who_would_be_silly_enough_to_make_a_json_schem_faker | ReferenceGenerator: "http://www.example.com#/$defs/me" |
| # | Root Generator |
| Reference | Generator |
|---|---|
| #/$defs/me/properties/suffering | ConstGenerator: "Oww" |
| #/$defs/me/properties/pain | ConstGenerator: "Suffering" |
| #/$defs/me | ObjectGenerator |
| # | Root Generator |
www.example.com
Root Document
Scheme 3: It's time to write a web crawlerπ·οΈ
| Reference | Generator |
|---|---|
| #/properties/who_would_be_silly_enough_to_make_a_json_schem_faker | ReferenceGenerator: "http://www.example.com#/$defs/me" |
| # | Root Generator |
| Reference | Generator |
|---|---|
| #/$defs/me/properties/suffering | ConstGenerator: "Oww" |
| #/$defs/me/properties/pain | ConstGenerator: "Suffering" |
| #/$defs/me | ObjectGenerator |
| # | Root Generator |
www.example.com
Root Document
Scheme 3: $ref...
{
"type": "object",
"properties": {
"value": { "type": "string" },
"next": { "$ref": "#/" }
}
}Scheme 3: $ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$ref$
{
"type": "object",
"properties": {
"value": { "type": "string" },
"next": { "$ref": "#/" }
}
}
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 0
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 1
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 2
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 3
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 4
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 5
Max Effort: 5
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 5
Max Effort: 5
Β―\_(γ)_/Β―
Scheme 3: Circular References
an asside on complexity
Object Generator
"some_array"
Array Generator(Min Len: 5)
Constant Generator: "test"
String Generator (min len: 5)
"some_string"
Generation Effort: 5
Max Effort: 5
Return
Scheme 3: Circular References
However when we need to merge
{
"allOf": [
{"$ref": "#/someCircularRef"}
]
}Scheme 3: Circular References
However when we need to merge
{
"$id": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": "..."
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}Scheme 3: Circular References
However when we need to merge
{
"$id": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": {
"type": "object",
"properties": {
"value": { "type": "string" },
"next": "..."
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}Scheme 3: Circular References
If you have any idea how this might be resolvable please let me know! *
(Tell me I am silly!)
Β
*(Currently in the process of looking into the approach used in https://github.com/udamir/allof-merge)
Scheme 3: $refs
Scheme 3: $refs

Please no more
scheme 2: Combination

scheme 2: Combination


scheme 2: Combination

scheme 2: Combination

Scheme 4: NOT

scheme 2: Combination
Scheme 4: NOT

My sanity

!
One Exclamation mark boi
scheme 2: Combination
Scheme 4: NOT
from a validators perspective
{
"not": {
"multipleOf": 5
}
}scheme 2: Combination
Scheme 4: NOT
from a validators perspective
{
"not": {
"multipleOf": 5
}
}Validate Against
scheme 2: Combination
Scheme 4: NOT
from a validators perspective
easy :)
Β
{
"not": {
"multipleOf": 5
}
}Validate Against
Invert result :)
scheme 2: Combination
Scheme 4: NOT
{
"minimum": 45,
"exclusiveMaximum": 64,
"multipleOf": 7,
"not": {
"exclusiveMinimum": 48,
"maximum": 55,
"multipleOf": 14
}
}From a generators perspective.
Just find the number easy :)
scheme 2: Combination
Scheme 4: Do it all again... backwards
func resolveBoundsFloat64(metadata *parserMetadata,
minFieldName string,
maxFieldName string,
minPtr *float64, minExclusivePtr *float64,
maxPtr *float64, maxExclusivePtr *float64,
notMinPtr *float64, notMinExclusivePtr *float64,
notMaxPtr *float64, notMaxExclusivePtr *float64,
offsetIncrement float64,
) (*float64, *float64, *float64, *float64)
scheme 2: Combination
Scheme 4: notMerging
- Merge what we can by logically resolving constraints inside and outside of the not.
- Apply constraints to the output of the generated output where not possible and brute force generate for a number of different iterations.
scheme 2: Combination
Scheme 4: notMerging
{
"enum": [
"foo", "baz", "bar"
],
"not": {
"enum": ["baz", "bar"]
}
}scheme 2: Combination
Scheme 4: notMerging
{
"enum": [
"foo", "baz", "bar"
],
"not": {
"enum": ["baz", "bar"]
}
}{
"const": "foo"
}Simplifies to
scheme 2: Combination
Scheme 4: notMerging
{
"minimum": 45,
"exclusiveMaximum": 64,
"multipleOf": 7,
"not": {
"exclusiveMinimum": 48,
"maximum": 55,
"multipleOf": 14
}
}scheme 2: Combination
Scheme 4: notMerging
{
"minimum": 45,
"exclusiveMaximum": 64,
"multipleOf": 7,
"not": {
"exclusiveMinimum": 48,
"maximum": 55,
"multipleOf": 14
}
}{
"const": 63
}Simplifies to
scheme 2: Combination
Scheme 4: Not not
{
"not": {
"not": {
"not": {
"not": {
"not": {
"type": "object",
"required": ["children"]
}
}
}
}
}
}scheme 2: Combination
Scheme 4: Not not
{
"not": {
"not": {
"type": "object",
"required": ["children"]
}
}
}scheme 2: Combination
Scheme 4: Not not
{
"not": {
"not": {
"type": "object",
"required": ["children"]
}
}
}Is Equal To
{
"type": "object",
"required": ["children"]
}
scheme 2: Combination
Scheme 4: Not not
{
"not": {
"not": {
"not": {
"not": {
"not": {
"type": "object",
"required": ["children"]
}
}
}
}
}
}Merge alternating (excluding not)
using the allOf merge algorithm, leaving a single node and notNode
Scheme 4: not
Scheme 4: not

Still need to resolve merging for `if`, `then`, `else`, `oneOf`, `allOf`, `anyOf`
Anything else?
Anything else?
{
"mimimum": 20,
"maximum: 10
}Anything else?
{
"mimimum": 20,
"maximum": 10
}wat?
Anything else?
{
"mimimum": 20,
"maximum": 10
}Scheme 5: where logic breaks down...
{"mimimum": 20}{"maximum": 10}15Scheme 5: where logic breaks down...
β
{"mimimum": 20}{"maximum": 10}15Scheme 5: where logic breaks down...
β
{"mimimum": 20}{"maximum": 10}15Scheme 5: where logic breaks down...
β
β
Not valid
15Scheme 5: where logic breaks down...
for a generator
{
"mimimum": 20,
"maximum": 10
}Error? Null?
Scheme 5: where logic breaks down...
To really exemplify the point lets have a quiz
Note that for the purposes of this quiz an "Illogical schema" is one where traits of the schema invalidate each other. Or we could not pass in data other than an empty value to satisfy the schema.
Β
(If I have missed anything assume you need to match all the constraints given to have the schema validate)
Is the schema illogical?
{
"pattern": "\\d+\\w{2}",
"maxLength": 3
}Is the schema illogical?
{
"pattern": "\\d+\\w{2}",
"maxLength": 3
}Logical β
Matches at min a 3 char sequence
Matches up to 3 chars
Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"not": {
"minimum": 9999
}
}Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"not": {
"minimum": 9999
}
}Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"not": {
"minimum": 9999
}
}Illogical β
Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"maximum": 9999
}Illogical β
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"not": {
"minimum": 9999
}
}Simplifies To
Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"maximum": 9999
}Illogical β
Smallest possible integer multiple 9999
Is the schema illogical?
{
"type": "integer",
"multipleOf": 0.9999,
"mimimum": 0,
"maximum": 9999
}Illogical β
Smallest possible integer multiple 9999
Not valid against
("exclusiveMaximim" would)
Is the schema illogical?
{
"type": "array",
"allOf": [
{
"items": {
"enum": [
"foo",
"baz"
]
},
},
{
"items": {
"const": "foo",
}
},
{
"items": {
"const": "baz"
}
}
]
}Is the schema illogical?
Illogical β
{
"type": "array",
"allOf": [
{
"items": {
"enum": [
"foo",
"baz"
]
},
},
{
"items": {
"const": "foo",
}
},
{
"items": {
"const": "baz"
}
}
]
}Is the schema illogical?
{
"type": "array",
"allOf": [
{
"items": {
"enum": [
"foo",
"baz"
]
},
},
{
"items": {
"const": "foo",
}
},
{
"items": {
"const": "baz"
}
}
]
}Illogical β
Mutually exclusive
Is the schema illogical?
{
"$ref": "#/$defs/node",
"$defs": {
"node": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/node"
}
},
},
"required": [
"children"
]
}
}
}Text
Is the schema illogical?
{
"$ref": "#/$defs/node",
"$defs": {
"node": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/node"
}
},
},
"required": [
"children"
]
}
}
}Illogical β
Text
Is the schema illogical?
{
"$ref": "#/$defs/node",
"$defs": {
"node": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"children": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/node"
}
},
},
"required": [
"children"
]
}
}
}Illogical β
Text
Forces infinite
recursion
Scheme 5: Illogical Schemas
What we can do about this?
- Given we are already compiling the schema into an intermediary generator tree we can:
- Validate certain logical constraints during compilation.Β
- In cases where schema traversal is required mixing earlier talked about cost functions with cyclic reference checks
- Setting a maximum reference resolution depth during generation
Scheme 5: Illogical Schemas
Scheme 5: Illogical Schemas

As we go forwards
There are a few other things:
-
if/then/elseclauses are fairly difficult to resolve during merging and have a number of different bugs - Some verbiage from newer schema drafts needs to be implemented
$dynamicReference, $depentantProperties, ect... - Not has restrictions.
- Mixing of certain properties like "pattern" and "format" (In assertion mode) can be fairly difficult to reconcile. Or "pattern" with "min/maxLength" due to enforcing one verses the other.
Why Bother using a JSON schema faker?
Testing

JSON Schema Faker
Testing


JSON Schema Faker
JSON Schema Validator
Testing


Generate
Validate
JSON Schema Faker
X100
JSON Schema Validator
Testing


Generate
Validate
JSON Schema Faker
X100
JSON Schema Validator (s)



Testing for differences by fuzzing
β
β
β
β
Exploring Your Schemas
{
"properties": {
"sometimes_schemas": {
"const": "can be very complex"
}
}
}{"sometimes_schemas":"can be very complex"}Having a JSON schema faker generate many samples can be a great way of understanding what a schema is looking for or find issues withΒ
Test Mocks
The original use case was a need for high quality random data, something JSON schema is set to do really well as it already aims to cover the shape of pretty much any structured data format.
Β
This is one of the main reasons for using json schema
{schema}
{data}
π¨You have gone more than 15 minutes without talking about AIπ¨
Why Not Use AI?
*
* For the problem:
"Generate random JSON to satisfy a given JSON schema"
Caveat .txtΒ are trying to solve fundamentally a different problem in a really cool way

Caveat .txtΒ are trying to solve fundamentally a different problem in a really cool way

Only speak with this JSON schema
Caveat .txtΒ are trying to solve fundamentally a different problem in a really cool way

Only speak with this JSON schema
{
"structured": "data",
"with": "purpose"
}- MCP
- Anything that really requires well structure data
Feed into
Why Not Use AI?

Bad Output

[More bugs to fix yaaay]

use .txt, use a larger model Β―\_(γ)_/Β―?
Bad Output
Why Not Use AI?


100x faster
Whats Next
- Feature selection needs to be configurable based on a given JSON Schema draft
- Many many many small edge cases surrounding bad merges need to be resolved and a respect that not all constraints can be reasonably resolved together.Β
- Ideally all schema vocabulary will be supported or explicitly disallowed in combination.
Credits
- Β Mascot design by @Iroshi_ CC0
- Most other Images CC0 Wikimedia commons
Β

https://github.com/ryanolee/go-chaff
Bugs and PR's always appreciated :)

Contributions
Thank you and Questions?
Go Chaff
By Rizza
Go Chaff
- 83