Skip to main content
Version: Next

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.SchemaLLM[T]()Get JSON Schema for LLM APIs (no $schema or $id)
pedantigo.SchemaJSONLLM[T]()LLM schema as bytes (no $schema or $id)
pedantigo.SchemaOpenAPI[T]()Get OpenAPI 3.1 component schema
pedantigo.SchemaJSONOpenAPI[T]()OpenAPI 3.1 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.SchemaLLM()LLM schema (no $schema or $id)
v.SchemaJSONLLM()LLM schema as bytes (no $schema or $id)
v.SchemaOpenAPI()OpenAPI 3.1 schema
v.SchemaJSONOpenAPI()OpenAPI 3.1 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 json:",omitempty" Interact

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

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. json:",omitempty" is for marshaling output - Controls whether zero-value fields are omitted when serialising a struct to JSON. It does not affect validation or schema generation.
  2. required controls both - Affects the schema required array AND validation (field must be present in JSON input).
  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.


omitempty as a Pedantigo Validation Constraint

In addition to Go's json:",omitempty" marshaling tag, Pedantigo supports omitempty as a validation constraint in the pedantigo struct tag itself.

type SearchRequest struct {
ActorID string `pedantigo:"omitempty,max=64,required_with=ActorType"`
ActorType string `pedantigo:"omitempty,oneof=user agent system,required_with=ActorID"`
}

Semantics:

When a field is tagged with pedantigo:"omitempty,..." and its value is the zero value for its type (empty string, 0, false, nil pointer, etc.):

  • Regular constraints (min, max, oneof, email, url, etc.) are skipped — the field is treated as intentionally absent.
  • Cross-field constraints (required_with, required_if, eqfield, nefield, etc.) always run, regardless of zero-value status, to preserve relational validation semantics.

When the field has a non-zero value, all constraints run normally.

Example — symmetric optional pair:

type Actor struct {
// Both are optional. If one is provided, the other must be too.
ActorID string `pedantigo:"omitempty,max=64,required_with=ActorType"`
ActorType string `pedantigo:"omitempty,oneof=user agent system,required_with=ActorID"`
}
ActorIDActorTypeResult
""""Valid — both zero, regular constraints skipped
"user123""user"Valid — both non-zero, all constraints run
"user123"""Error on ActorTyperequired_with=ActorID fires because ActorID is non-zero
"""user"Error on ActorIDrequired_with=ActorType fires because ActorType is non-zero
"user123""invalid"Error on ActorTypeoneof fires because field is non-zero

Difference from json:",omitempty":

TagWhere it appearsEffect
json:",omitempty"JSON struct tagControls marshaling output: omits the field from JSON if its value is zero
pedantigo:"omitempty"Pedantigo constraintControls validation: skips regular constraints when the field value is zero

Both can be combined:

// Omitted from JSON output when zero AND skips validation when zero
Region string `json:"region,omitempty" pedantigo:"omitempty,oneof=us-east-1 us-west-2 eu-west-1"`

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: