github.com/kaptinlin/jsonschema@v0.4.6/examples/unmarshaling/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/kaptinlin/jsonschema"
     9  )
    10  
    11  // Static errors for linter compliance
    12  var (
    13  	ErrValidationFailed = errors.New("validation failed")
    14  	ErrUnmarshalFailed  = errors.New("unmarshal failed")
    15  )
    16  
    17  type User struct {
    18  	Name    string `json:"name"`
    19  	Age     int    `json:"age"`
    20  	Country string `json:"country"`
    21  	Active  bool   `json:"active"`
    22  }
    23  
    24  func main() {
    25  	// Schema with default values
    26  	compiler := jsonschema.NewCompiler()
    27  	schema, err := compiler.Compile([]byte(`{
    28  		"type": "object",
    29  		"properties": {
    30  			"name": {"type": "string", "minLength": 1},
    31  			"age": {"type": "integer", "minimum": 0},
    32  			"country": {"type": "string", "default": "US"},
    33  			"active": {"type": "boolean", "default": true}
    34  		},
    35  		"required": ["name", "age"]
    36  	}`))
    37  	if err != nil {
    38  		log.Fatal(err)
    39  	}
    40  
    41  	fmt.Println("Validation + Unmarshaling with Defaults")
    42  	fmt.Println("=======================================")
    43  
    44  	// Example 1: Valid data - validate first, then unmarshal
    45  	fmt.Println("1. Valid data with missing optional fields:")
    46  	data1 := []byte(`{"name": "Alice", "age": 25}`)
    47  
    48  	// Step 1: Validate
    49  	result := schema.Validate(data1)
    50  	if result.IsValid() {
    51  		// Step 2: Unmarshal with defaults
    52  		var user1 User
    53  		err = schema.Unmarshal(&user1, data1)
    54  		if err != nil {
    55  			log.Fatal(err)
    56  		}
    57  		fmt.Printf("   ✅ Valid data unmarshaled: %+v\n", user1)
    58  	} else {
    59  		fmt.Printf("   ❌ Validation failed: %v\n", result.Errors)
    60  	}
    61  
    62  	// Example 2: Map with some defaults overridden
    63  	fmt.Println("\n2. Map with some values provided:")
    64  	mapData := map[string]interface{}{
    65  		"name":    "Bob",
    66  		"age":     30,
    67  		"country": "Canada", // Override default
    68  	}
    69  
    70  	result = schema.Validate(mapData)
    71  	if result.IsValid() {
    72  		var user2 User
    73  		err = schema.Unmarshal(&user2, mapData)
    74  		if err != nil {
    75  			log.Fatal(err)
    76  		}
    77  		fmt.Printf("   ✅ Valid data unmarshaled: %+v\n", user2)
    78  	} else {
    79  		fmt.Printf("   ❌ Validation failed: %v\n", result.Errors)
    80  	}
    81  
    82  	// Example 3: Invalid data - validation fails but unmarshal still works
    83  	fmt.Println("\n3. Invalid data handling:")
    84  	invalidData := []byte(`{"name": "", "age": -5}`)
    85  
    86  	result = schema.Validate(invalidData)
    87  	if !result.IsValid() {
    88  		fmt.Println("   ❌ Validation failed:")
    89  		for field, err := range result.Errors {
    90  			fmt.Printf("     - %s: %s\n", field, err.Message)
    91  		}
    92  
    93  		// Unmarshal still works (no validation performed)
    94  		var user3 User
    95  		err = schema.Unmarshal(&user3, invalidData)
    96  		if err != nil {
    97  			fmt.Printf("   ❌ Unmarshal error: %v\n", err)
    98  		} else {
    99  			fmt.Printf("   ℹ️  Unmarshal succeeded despite validation failure: %+v\n", user3)
   100  		}
   101  	}
   102  
   103  	// Example 4: Missing required field
   104  	fmt.Println("\n4. Missing required field:")
   105  	missingData := []byte(`{"age": 25}`)
   106  
   107  	result = schema.Validate(missingData)
   108  	if !result.IsValid() {
   109  		fmt.Println("   ❌ Validation failed (missing required field):")
   110  		for field, err := range result.Errors {
   111  			fmt.Printf("     - %s: %s\n", field, err.Message)
   112  		}
   113  
   114  		// Unmarshal still applies defaults for optional fields
   115  		var user4 User
   116  		err = schema.Unmarshal(&user4, missingData)
   117  		if err != nil {
   118  			fmt.Printf("   ❌ Unmarshal error: %v\n", err)
   119  		} else {
   120  			fmt.Printf("   ℹ️  Unmarshal with defaults: %+v\n", user4)
   121  		}
   122  	}
   123  
   124  	// Example 5: To map instead of struct
   125  	fmt.Println("\n5. Unmarshal to map:")
   126  	data5 := []byte(`{"name": "Charlie", "age": 35}`)
   127  
   128  	result = schema.Validate(data5)
   129  	if result.IsValid() {
   130  		var resultMap map[string]interface{}
   131  		err = schema.Unmarshal(&resultMap, data5)
   132  		if err != nil {
   133  			log.Fatal(err)
   134  		}
   135  		fmt.Printf("   ✅ Valid data to map: %+v\n", resultMap)
   136  	}
   137  
   138  	// Example 6: Recommended pattern for production
   139  	fmt.Println("\n6. Recommended production pattern:")
   140  	productionData := []byte(`{"name": "Diana", "age": 28, "country": "France"}`)
   141  
   142  	if err := processUser(schema, productionData); err != nil {
   143  		fmt.Printf("   ❌ Processing failed: %v\n", err)
   144  	} else {
   145  		fmt.Println("   ✅ User processed successfully")
   146  	}
   147  }
   148  
   149  // processUser demonstrates the recommended pattern for production use
   150  func processUser(schema *jsonschema.Schema, data []byte) error {
   151  	// Step 1: Always validate first
   152  	result := schema.Validate(data)
   153  	if !result.IsValid() {
   154  		return fmt.Errorf("%w: %v", ErrValidationFailed, result.Errors)
   155  	}
   156  
   157  	// Step 2: Unmarshal validated data
   158  	var user User
   159  	if err := schema.Unmarshal(&user, data); err != nil {
   160  		return fmt.Errorf("%w: %w", ErrUnmarshalFailed, err)
   161  	}
   162  
   163  	// Step 3: Process the user
   164  	fmt.Printf("     Processing user: %s from %s (active: %t)\n",
   165  		user.Name, user.Country, user.Active)
   166  
   167  	return nil
   168  }