Skip to main content
Version: 0.1.2

Initialization & Configuration Reference

Complete reference for all Pedantigo initialization methods and configuration options.

Quick Comparison

APIUse CaseCustomizableLink
Simple API80% of casesNo (uses defaults)
Validator APICustom optionsYes
Stream ParserLLM/partial JSONYes
Union ValidatorDiscriminated unionsYes

Simple API

Global functions with automatic caching. Uses DefaultValidatorOptions().

FunctionDescription
pedantigo.Unmarshal[T](data)Unmarshal JSON and validate
pedantigo.Validate[T](obj)Validate existing struct
pedantigo.NewModel[T](input)Create from map/struct
pedantigo.Schema[T]()Get JSON Schema
pedantigo.SchemaJSON[T]()Get JSON Schema as bytes
pedantigo.SchemaOpenAPI[T]()Get OpenAPI-compatible schema
pedantigo.SchemaJSONOpenAPI[T]()OpenAPI schema as bytes
pedantigo.Marshal[T](obj)Marshal struct to JSON
pedantigo.MarshalWithOptions[T](obj, opts)Marshal with options
pedantigo.Dict[T](obj)Convert to map[string]any
// Simple API: automatic defaults, global caching
user, err := pedantigo.Unmarshal[User](jsonData)

// Equivalent to:
validator := pedantigo.New[User](pedantigo.DefaultValidatorOptions())
user, err := validator.Unmarshal(jsonData)

See Simple API Reference for detailed examples.


Validator API

Explicit validator instances with custom options.

Creating a Validator

FunctionDescription
pedantigo.New[T]()Create with defaults
pedantigo.New[T](opts)Create with custom options

Validator Methods

MethodDescription
v.Unmarshal(data)Unmarshal with validation
v.Validate(obj)Validate struct
v.NewModel(input)Create from map/struct
v.Schema()Get JSON Schema
v.SchemaJSON()Schema as bytes
v.SchemaOpenAPI()OpenAPI schema
v.SchemaJSONOpenAPI()OpenAPI as bytes
v.Marshal(obj)Marshal to JSON
v.MarshalWithOptions(obj, opts)Marshal with options
v.Dict(obj)Convert to map
// Create validator with custom options
validator := pedantigo.New[User](pedantigo.ValidatorOptions{
StrictMissingFields: true,
ExtraFields: pedantigo.ExtraForbid,
})

// Reuse the same validator multiple times
user1, err := validator.Unmarshal(data1)
user2, err := validator.Unmarshal(data2)

// Schema is cached, so subsequent calls are very fast
schema := validator.Schema()

Use the Validator API when you need:

  • Custom options
  • Schema caching for high-performance scenarios
  • Consistent behavior across multiple unmarshal calls
  • Explicit control over validator creation

See Validator Reference for detailed examples.


Stream Parser API

For partial/streaming JSON (ideal for LLM outputs).

FunctionDescription
pedantigo.NewStreamParser[T]()Create with defaults
pedantigo.NewStreamParser[T](opts)Create with custom options
pedantigo.NewStreamParserWithValidator[T](v)Use existing validator

Stream Parser Methods

MethodDescription
sp.Feed(chunk)Process JSON chunk, returns partial result

See Streaming Validation for detailed examples.


Union Validator API

For discriminated unions with type switching.

FunctionDescription
pedantigo.NewUnion[T](opts)Create union validator

Union Validator Methods

MethodDescription
uv.Unmarshal(data)Unmarshal and return variant

See Unions for detailed examples.


ValidatorOptions

Configuration struct for Validator API, Stream Parser, and Union Validator.

type ValidatorOptions struct {
// StrictMissingFields controls behavior for missing fields
// Default: true (missing fields without defaults cause errors)
StrictMissingFields bool

// ExtraFields controls how unknown JSON fields are handled
// Default: ExtraIgnore (unknown fields are silently ignored)
ExtraFields ExtraFieldsMode

// TagName overrides the global struct tag name for this validator instance
// Default: "" (uses global default "pedantigo")
TagName string
}
FieldTypeDefaultDescription
StrictMissingFieldsbooltrueMissing field handling
ExtraFieldsExtraFieldsModeExtraIgnoreUnknown field handling
TagNamestring""Custom tag name

Default Options

The default options are optimized for safety and strictness:

pedantigo.DefaultValidatorOptions()
// Returns: ValidatorOptions{
// StrictMissingFields: true,
// ExtraFields: ExtraIgnore,
// TagName: "", // Uses global default "pedantigo"
// }

StrictMissingFields Option

Controls whether missing fields (fields not present in JSON) cause validation errors.

StrictMissingFields: true (Default)

Missing fields without defaults are validation errors:

type Config struct {
Host string `json:"host" pedantigo:"required"`
Port int `json:"port"` // No default
}

jsonData := []byte(`{"host":"localhost"}`)
validator := pedantigo.New[Config](pedantigo.ValidatorOptions{
StrictMissingFields: true,
})

config, err := validator.Unmarshal(jsonData)
// Error: port field is missing and has no default

Use this mode when you want to:

  • Catch missing fields as validation errors
  • Ensure all fields are explicitly provided in JSON
  • Enforce strict API contracts

StrictMissingFields: false

Missing fields without defaults are left as zero values:

type Config struct {
Host string `json:"host" pedantigo:"required"`
Port int `json:"port"` // Zero value: 0
}

jsonData := []byte(`{"host":"localhost"}`)
validator := pedantigo.New[Config](pedantigo.ValidatorOptions{
StrictMissingFields: false,
})

config, err := validator.Unmarshal(jsonData)
// Success: Port is set to 0 (zero value)
fmt.Println(config.Port) // Output: 0

Use this mode when you want to:

  • Allow partial JSON input (missing fields get zero values)
  • Make most fields optional
  • Support backwards-compatible API evolution

How required and omitempty Interact

Understanding how Go's json:",omitempty" interacts with Pedantigo's required tag:

Struct TagsIn JSON Schema requiredUnmarshal Behavior
pedantigo:"required"YesError if field missing from JSON
json:",omitempty" onlyNoZero value if missing (valid)
Both required + omitemptyYesError if missing (required wins)
Pointer *T + requiredYesError if missing or null
Pointer *T without requiredNonil if missing (valid)
Non-pointer without requiredNoZero value if missing

Key Points:

  1. omitempty is for output - It controls JSON marshaling, not schema generation
  2. required controls both - Affects schema required array AND validation
  3. Pointers enable true optionality - Use *T when you need to distinguish "missing" from "zero"
type Config struct {
// Required: must be in JSON, appears in schema required array
Host string `json:"host" pedantigo:"required"`

// Optional with omitempty: omitted from output if zero, NOT in required array
Port int `json:"port,omitempty"`

// Optional pointer: nil if missing, distinguishes missing from zero
Timeout *int `json:"timeout,omitempty"`

// Required + omitempty: must be in JSON input, omitted from output if zero
Name string `json:"name,omitempty" pedantigo:"required"`
}
tip

See Schema Generation to understand how these tags affect the generated JSON Schema.


ExtraFields Option

Controls how unknown (extra) JSON fields are handled during unmarshaling.

ExtraIgnore (Default)

Unknown JSON fields are silently ignored:

type User struct {
Name string `json:"name"`
Email string `json:"email"`
}

jsonData := []byte(`{
"name": "Alice",
"email": "alice@example.com",
"age": 30,
"phone": "555-1234"
}`)

validator := pedantigo.New[User](pedantigo.ValidatorOptions{
ExtraFields: pedantigo.ExtraIgnore,
})

user, err := validator.Unmarshal(jsonData)
// Success: age and phone fields are ignored

Use this mode when you want to:

  • Accept JSON with additional fields (future-proofing)
  • Ignore client-provided metadata
  • Support flexible API clients

ExtraForbid

Unknown JSON fields cause validation errors:

type User struct {
Name string `json:"name"`
Email string `json:"email"`
}

jsonData := []byte(`{
"name": "Alice",
"email": "alice@example.com",
"age": 30
}`)

validator := pedantigo.New[User](pedantigo.ValidatorOptions{
ExtraFields: pedantigo.ExtraForbid,
})

user, err := validator.Unmarshal(jsonData)
// Error: unknown field "age" in JSON
var validationErr *pedantigo.ValidationError
if errors.As(err, &validationErr) {
fmt.Println(validationErr.Errors[0].Message)
// Output: unknown field in JSON
}

Use this mode when you want to:

  • Prevent typos in client input
  • Detect API misuse
  • Enforce strict schema compliance

ExtraAllow

Unknown JSON fields are captured and stored in a designated map[string]any field:

type User struct {
Name string `json:"name"`
Email string `json:"email"`
Extras map[string]any `json:"-" pedantigo:"extra_fields"`
}

jsonData := []byte(`{
"name": "Alice",
"email": "alice@example.com",
"age": 30,
"phone": "555-1234"
}`)

validator := pedantigo.New[User](pedantigo.ValidatorOptions{
ExtraFields: pedantigo.ExtraAllow,
})

user, err := validator.Unmarshal(jsonData)
// Success: unknown fields are captured
fmt.Println(user.Extras["age"]) // Output: 30
fmt.Println(user.Extras["phone"]) // Output: 555-1234

Requirements:

  1. Struct must have an extra_fields tagged field:

    Extras map[string]any `json:"-" pedantigo:"extra_fields"`
  2. The field type must be map[string]any (or map[string]interface{})

  3. Fail-fast validation: If ExtraAllow is set but no extra_fields field exists, New[T]() panics at startup

Nested Struct Handling:

Each struct level independently handles its own extras. If a nested struct has an extra_fields field, it captures extras at that level:

type Address struct {
City string `json:"city"`
Extras map[string]any `json:"-" pedantigo:"extra_fields"`
}

type User struct {
Name string `json:"name"`
Address Address `json:"address"`
Extras map[string]any `json:"-" pedantigo:"extra_fields"`
}

jsonData := []byte(`{
"name": "Alice",
"extra_top": "top-level extra",
"address": {
"city": "NYC",
"extra_nested": "nested extra"
}
}`)

// Top-level extras go to User.Extras
// Nested address extras go to Address.Extras

If a nested struct doesn't have an extra_fields field, extras at that level are silently ignored (but the top-level still requires the field when ExtraAllow is set).

Round-Trip Support:

Extra fields are preserved during marshaling:

user, _ := validator.Unmarshal(jsonData)
// user.Extras contains captured extras

roundTripJSON, _ := validator.Marshal(user)
// roundTripJSON includes both struct fields AND extras

ExtraAllow: Real-World Use Cases

Use Case 1: Multi-Version API Support

When evolving APIs, newer clients may send fields that older server versions don't recognize. ExtraAllow preserves these fields for:

  • Forward compatibility: Accept fields from newer clients
  • Proxy/gateway scenarios: Pass through unknown fields to downstream services
  • Gradual migration: Log unknown fields to understand client adoption
type UserV1 struct {
Name string `json:"name" pedantigo:"required"`
Email string `json:"email" pedantigo:"required,email"`
Extras map[string]any `json:"-" pedantigo:"extra_fields"`
}

// Accept requests from V2 clients that include "profile_picture", "preferences", etc.
// These are captured in Extras for logging/forwarding without breaking V1 logic.
validator := pedantigo.New[UserV1](pedantigo.ValidatorOptions{
ExtraFields: pedantigo.ExtraAllow,
})

user, _ := validator.Unmarshal(requestBody)
if len(user.Extras) > 0 {
log.Printf("Client sent unknown fields: %v", maps.Keys(user.Extras))
// Forward to downstream service that may understand these fields
}

Further Reading:

Use Case 2: LLM Output Capture for Prompt Evaluation

When using LLMs with structured output (JSON mode), models may include unexpected fields. ExtraAllow enables:

  • Prompt debugging: Identify when models add unrequested fields
  • Model accuracy evaluation: Track field adherence across prompts
  • Prompt iteration: Use captured extras to refine instructions
type LLMResponse struct {
Answer string `json:"answer" pedantigo:"required"`
Confidence float64 `json:"confidence"`
Extras map[string]any `json:"-" pedantigo:"extra_fields"`
}

validator := pedantigo.New[LLMResponse](pedantigo.ValidatorOptions{
ExtraFields: pedantigo.ExtraAllow,
})

// Parse LLM output
response, err := validator.Unmarshal(llmOutput)
if err != nil {
// Handle validation error (missing required fields, etc.)
}

// Track extra fields for prompt improvement
if len(response.Extras) > 0 {
log.Printf("LLM included unexpected fields: %v", response.Extras)
// Metrics for evaluating model accuracy
metrics.RecordExtraFields(modelName, promptID, response.Extras)
// Example: {"reasoning": "...", "sources": [...]} - fields LLM added on its own
}

Further Reading:


TagName Option

Override the struct tag name for a specific validator instance.

Default: Uses global tag name ("pedantigo" or set via SetTagName())

ValueBehavior
"" (empty)Use global default
"validate"Use go-playground/validator style
"binding"Use gin-gonic style
// Use go-playground/validator style tags
validator := pedantigo.New[User](pedantigo.ValidatorOptions{
TagName: "validate",
})

type User struct {
Name string `json:"name" validate:"required,min=3"`
}

Use Cases:

  • Migration from other validation libraries
  • Team conventions requiring specific tag names
  • Coexistence with other libraries using pedantigo tag

Further Reading:


Choosing the Right Options

ScenarioStrictMissingFieldsExtraFieldsReason
REST API with strict contractstrueExtraForbidPrevent bugs from typos/API misuse
Configuration file parsingfalseExtraIgnoreAllow partial configs, future-proof
During API migrationfalseExtraIgnoreAccept old and new client versions
Public web APItrueExtraIgnoreStrict input, future-proof
Internal service APItrueExtraForbidStrict both ways
Webhook receiverfalseExtraIgnoreAccept any fields from sender
LLM output parsingtrueExtraAllowCapture unexpected model outputs
API gateway/proxyfalseExtraAllowPass through unknown fields

Option Combinations

Most Strict

pedantigo.ValidatorOptions{
StrictMissingFields: true,
ExtraFields: pedantigo.ExtraForbid,
}
// Best for: REST API validation, ensuring exact schema match

Most Lenient

pedantigo.ValidatorOptions{
StrictMissingFields: false,
ExtraFields: pedantigo.ExtraIgnore,
}
// Best for: Configuration parsing, webhook receivers, flexible APIs

Balanced (Default)

pedantigo.DefaultValidatorOptions()
// StrictMissingFields: true
// ExtraFields: ExtraIgnore
// Best for: General-purpose APIs, good balance of safety and flexibility

Capture Everything

pedantigo.ValidatorOptions{
StrictMissingFields: false,
ExtraFields: pedantigo.ExtraAllow,
}
// Best for: LLM output parsing, API gateways, forward-compatible services

Complete Examples

API Server with Strict Validation

package main

import (
"errors"
"log"

"github.com/smrutai/pedantigo"
)

type CreateUserRequest struct {
Username string `json:"username" pedantigo:"required,min=3,max=20"`
Email string `json:"email" pedantigo:"required,email"`
Password string `json:"password" pedantigo:"required,min=8"`
}

// API validator: strict about extra fields, requires all fields
var apiValidator = pedantigo.New[CreateUserRequest](
pedantigo.ValidatorOptions{
StrictMissingFields: true,
ExtraFields: pedantigo.ExtraForbid,
},
)

func handleCreateUser(jsonData []byte) (*CreateUserRequest, error) {
req, err := apiValidator.Unmarshal(jsonData)
if err != nil {
var validationErr *pedantigo.ValidationError
if errors.As(err, &validationErr) {
return nil, validationErr
}
return nil, err
}
return req, nil
}

Configuration File Parsing (Lenient)

type AppConfig struct {
Database struct {
Host string `json:"host" pedantigo:"required"`
Port int `json:"port" pedantigo:"default=5432"`
Username string `json:"username" pedantigo:"required"`
Password string `json:"password" pedantigo:"required"`
} `json:"database"`
Server struct {
Addr string `json:"addr" pedantigo:"default=0.0.0.0:8080"`
} `json:"server"`
}

// Config validator: lenient about missing fields, allows extra fields
var configValidator = pedantigo.New[AppConfig](
pedantigo.ValidatorOptions{
StrictMissingFields: false,
ExtraFields: pedantigo.ExtraIgnore,
},
)

Migration-Friendly API

type UserV2 struct {
ID string `json:"id" pedantigo:"required,uuid"`
Username string `json:"username" pedantigo:"required"`
Email string `json:"email" pedantigo:"required,email"`
Status string `json:"status" pedantigo:"default=active"`
}

// Migration validator: accept both old and new client requests
var migrationValidator = pedantigo.New[UserV2](
pedantigo.ValidatorOptions{
StrictMissingFields: false,
ExtraFields: pedantigo.ExtraIgnore,
},
)

Further Reading

Go Documentation:

Specifications:

LLM Structured Outputs: