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 }