A robust, performant Golang library for generating random, schema-compliant JSON data from JSON Schema definitions. Perfect for testing, fuzzing, and mock data generation.
- ✅ JSON Schema Compliant: Supports Draft 2020-12 and Draft-07
- ✅ Realistic Fake Data: Uses gofakeit for generating realistic mock data
- ✅ Deterministic Generation: Seedable random generation with sorted property iteration for reproducible results
- ✅ Type Safe: Strong typing with
json.RawMessage-backed polymorphic fields - ✅ Comprehensive Validation: Collects all schema errors via
ValidationErrors, validates negative constraints, required-in-properties, and more - ✅ Context Support: Full
context.Contextpropagation for cancellation and timeouts - ✅ Configurable: Control depth limits, field generation, and more
- ✅ Well Tested: 93%+ test coverage
go get github.com/sarathsp06/schemagenpackage main
import (
"fmt"
"log"
"github.com/sarathsp06/schemagen"
)
func main() {
schema := `{
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 3},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 18, "maximum": 100}
},
"required": ["name", "email"]
}`
gen := schemagen.NewGenerator()
result, err := gen.Generate([]byte(schema))
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)
// Output: map[age:42 email:john.doe@example.com name:Alice]
}gen := schemagen.NewGenerator().
SetSeed(12345). // For deterministic output
SetMaxDepth(10). // Limit recursion depth
SetGenerateAllFields(true) // Generate optional fields too| Option | Default | Description |
|---|---|---|
SetSeed(int64) |
Current timestamp | Set seed for deterministic generation |
SetMaxDepth(int) |
10 | Maximum recursion depth for nested objects |
SetGenerateAllFields(bool) |
false | Generate all fields vs. only required ones |
A Generator instance is not safe for concurrent use by multiple goroutines. Each goroutine should create its own Generator instance:
// Correct: each goroutine gets its own generator
for i := 0; i < 10; i++ {
go func() {
gen := schemagen.NewGenerator()
result, _ := gen.Generate([]byte(schema))
// ...
}()
}| Keyword | Support | Description |
|---|---|---|
type |
✅ | Single or array of types: string, number, integer, boolean, object, array, null |
enum |
✅ | Pick random value from enumerated list |
const |
✅ | Return exact constant value |
| Keyword | Support | Example |
|---|---|---|
minLength |
✅ | {"type": "string", "minLength": 5} |
maxLength |
✅ | {"type": "string", "maxLength": 10} |
pattern |
✅ | {"type": "string", "pattern": "^[0-9]{5}$"} |
format |
✅ | See Supported Formats |
| Keyword | Support | Example |
|---|---|---|
minimum |
✅ | {"type": "integer", "minimum": 0} |
maximum |
✅ | {"type": "integer", "maximum": 100} |
exclusiveMinimum |
✅ | {"type": "number", "exclusiveMinimum": 0} |
exclusiveMaximum |
✅ | {"type": "number", "exclusiveMaximum": 1} |
multipleOf |
✅ | {"type": "integer", "multipleOf": 5} |
| Keyword | Support | Example |
|---|---|---|
properties |
✅ | Define object fields with schemas |
required |
✅ | List of required field names |
additionalProperties |
✅ | Allow extra properties (boolean or schema) |
| Keyword | Support | Example |
|---|---|---|
items |
✅ | Schema for array items (single or tuple) |
minItems |
✅ | {"type": "array", "minItems": 2} |
maxItems |
✅ | {"type": "array", "maxItems": 10} |
uniqueItems |
✅ | {"type": "array", "uniqueItems": true} |
| Keyword | Support | Behavior |
|---|---|---|
oneOf |
✅ | Randomly select one sub-schema |
anyOf |
✅ | Randomly select one sub-schema |
allOf |
✅ | Merge properties and required fields from all sub-schemas |
not |
✅ | Parsed and stored (used for validation tooling) |
The library uses gofakeit to generate realistic data for these formats:
| Format | Example Output |
|---|---|
uuid |
550e8400-e29b-41d4-a716-446655440000 |
email |
john.doe@example.com |
date-time |
2023-10-15T14:30:00Z |
date |
2023-10-15 |
time |
14:30:00 |
ipv4 |
192.168.1.1 |
ipv6 |
2001:0db8:85a3:0000:0000:8a2e:0370:7334 |
uri / url |
https://example.com/path |
hostname |
example.com |
schema := `{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"id": {"type": "string", "format": "uuid"},
"name": {"type": "string", "minLength": 3},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 18, "maximum": 120}
},
"required": ["id", "name", "email"]
},
"tags": {
"type": "array",
"items": {"type": "string"},
"minItems": 1,
"maxItems": 5
},
"active": {"type": "boolean"}
},
"required": ["user", "active"]
}`
gen := schemagen.NewGenerator()
result, _ := gen.Generate([]byte(schema))gen := schemagen.NewGenerator()
jsonBytes, err := gen.GenerateBytes([]byte(schema))
if err != nil {
log.Fatal(err)
}
fmt.Println(string(jsonBytes))func TestMyFunction(t *testing.T) {
gen := schemagen.NewGenerator().SetSeed(12345)
// Generate same data every time for reproducible tests
result1, _ := gen.Generate([]byte(schema))
// Reset with same seed
gen.SetSeed(12345)
result2, _ := gen.Generate([]byte(schema))
// result1 and result2 will be identical
}schema := `{
"oneOf": [
{
"type": "object",
"properties": {
"type": {"const": "user"},
"username": {"type": "string"}
}
},
{
"type": "object",
"properties": {
"type": {"const": "admin"},
"adminId": {"type": "integer"}
}
}
]
}`schema, err := schemagen.ParseSchema([]byte(`{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer", "minimum": 0, "maximum": 120}
},
"required": ["name"]
}`))
if err != nil {
log.Fatal(err)
}
gen := schemagen.NewGenerator()
result, err := gen.GenerateFromSchema(schema)ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
gen := schemagen.NewGenerator()
result, err := gen.GenerateWithContext(ctx, []byte(schema))
if err != nil {
// May be context.DeadlineExceeded or context.Canceled
log.Printf("Generation failed: %v", err)
}The library validates schemas and returns detailed errors for:
- Invalid JSON Schema syntax
- Conflicting constraints (e.g.,
minimum > maximum) - Negative constraint values (e.g., negative
minLength,maxItems) - Non-positive
multipleOf - Required fields not defined in
properties - Impossible
multipleOfranges - Maximum recursion depth exceeded
- Context cancellation and timeouts
Validate() returns a ValidationErrors value (which implements the error interface) containing all validation errors found in the schema, not just the first one:
schema, _ := schemagen.ParseSchema([]byte(`{
"type": "object",
"properties": {
"bad_string": {"type": "string", "minLength": 10, "maxLength": 5},
"bad_number": {"type": "number", "minimum": 100, "maximum": 50}
}
}`))
err := schema.Validate()
if err != nil {
// err is a ValidationErrors containing both constraint violations
fmt.Println(err) // "2 validation errors: [minLength (10) > maxLength (5); minimum (100) > maximum (50)]"
}gen := schemagen.NewGenerator().SetMaxDepth(3)
result, err := gen.Generate([]byte(deeplyNestedSchema))
if err != nil {
log.Printf("Generation failed: %v", err)
}- $ref: Reference resolution not yet implemented (future enhancement)
notkeyword: Parsed and stored but not enforced during generation
The library validates constraints and returns errors for impossible schemas:
// This will return an error
schema := `{"type": "integer", "minimum": 100, "maximum": 10}`Run the test suite:
go test -vRun tests with coverage:
go test -cover- github.com/brianvoe/gofakeit/v7 - Realistic fake data generation
- github.com/lucasjones/reggen - Regex pattern string generation
Contributions are welcome! Please feel free to submit issues or pull requests.
- Clone the repository
- Run tests:
go test -v - Make your changes
- Ensure all tests pass
- Submit a pull request
Future enhancements planned:
- Full
$refand definitions support -
notkeyword enforcement during generation - More format types (email variants, phone numbers, etc.)
- Custom format handlers
- Performance optimizations for large schemas
- CLI tool for generating test data
Built with: