github.com/kaptinlin/jsonschema@v0.4.6/constructor_test.go (about) 1 package jsonschema_test 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/kaptinlin/jsonschema" 8 ) 9 10 func Example_object() { 11 // Simple object schema using constructor API 12 schema := jsonschema.Object( 13 jsonschema.Prop("name", jsonschema.String(jsonschema.MinLen(1))), 14 jsonschema.Prop("age", jsonschema.Integer(jsonschema.Min(0))), 15 jsonschema.Required("name"), 16 ) 17 18 // Valid data 19 data := map[string]interface{}{ 20 "name": "Alice", 21 "age": 30, 22 } 23 24 result := schema.Validate(data) 25 fmt.Println("Valid:", result.IsValid()) 26 // Output: Valid: true 27 } 28 29 func Example_complexSchema() { 30 // Complex nested schema with validation keywords 31 userSchema := jsonschema.Object( 32 jsonschema.Prop("name", jsonschema.String( 33 jsonschema.MinLen(1), 34 jsonschema.MaxLen(100), 35 )), 36 jsonschema.Prop("age", jsonschema.Integer( 37 jsonschema.Min(0), 38 jsonschema.Max(150), 39 )), 40 jsonschema.Prop("email", jsonschema.Email()), 41 jsonschema.Prop("address", jsonschema.Object( 42 jsonschema.Prop("street", jsonschema.String(jsonschema.MinLen(1))), 43 jsonschema.Prop("city", jsonschema.String(jsonschema.MinLen(1))), 44 jsonschema.Prop("zip", jsonschema.String(jsonschema.Pattern(`^\d{5}$`))), 45 jsonschema.Required("street", "city"), 46 )), 47 jsonschema.Prop("tags", jsonschema.Array( 48 jsonschema.Items(jsonschema.String()), 49 jsonschema.MinItems(1), 50 jsonschema.UniqueItems(true), 51 )), 52 jsonschema.Required("name", "email"), 53 ) 54 55 // Test data 56 userData := map[string]interface{}{ 57 "name": "Alice", 58 "age": 30, 59 "email": "alice@example.com", 60 "address": map[string]interface{}{ 61 "street": "123 Main St", 62 "city": "Anytown", 63 "zip": "12345", 64 }, 65 "tags": []interface{}{"developer", "go"}, 66 } 67 68 result := userSchema.Validate(userData) 69 if result.IsValid() { 70 fmt.Println("User data is valid") 71 } else { 72 for field, err := range result.Errors { 73 fmt.Printf("Error in %s: %s\n", field, err.Message) 74 } 75 } 76 // Output: User data is valid 77 } 78 79 func Example_arraySchema() { 80 // Array schema with validation keywords 81 numbersSchema := jsonschema.Array( 82 jsonschema.Items(jsonschema.Number( 83 jsonschema.Min(0), 84 jsonschema.Max(100), 85 )), 86 jsonschema.MinItems(1), 87 jsonschema.MaxItems(10), 88 ) 89 90 validData := []interface{}{10, 20, 30} 91 result := numbersSchema.Validate(validData) 92 fmt.Println("Numbers valid:", result.IsValid()) 93 94 invalidData := []interface{}{-5, 150} // Out of range 95 result = numbersSchema.Validate(invalidData) 96 fmt.Println("Invalid numbers valid:", result.IsValid()) 97 // Output: 98 // Numbers valid: true 99 // Invalid numbers valid: false 100 } 101 102 func Example_enumAndConst() { 103 // Enum schema using enum keyword 104 statusSchema := jsonschema.Enum("active", "inactive", "pending") 105 106 result := statusSchema.Validate("active") 107 fmt.Println("Status valid:", result.IsValid()) 108 109 // Const schema using const keyword 110 versionSchema := jsonschema.Const("1.0.0") 111 112 result = versionSchema.Validate("1.0.0") 113 fmt.Println("Version valid:", result.IsValid()) 114 // Output: 115 // Status valid: true 116 // Version valid: true 117 } 118 119 func Example_oneOfAnyOf() { 120 // OneOf: exactly one schema must match 121 oneOfSchema := jsonschema.OneOf( 122 jsonschema.String(), 123 jsonschema.Integer(), 124 ) 125 126 result := oneOfSchema.Validate("hello") 127 fmt.Println("OneOf string valid:", result.IsValid()) 128 129 // AnyOf: at least one schema must match 130 anyOfSchema := jsonschema.AnyOf( 131 jsonschema.String(jsonschema.MinLen(5)), 132 jsonschema.Integer(jsonschema.Min(0)), 133 ) 134 135 result = anyOfSchema.Validate("hi") // Matches integer rule (length < 5 but is string) 136 fmt.Println("AnyOf short string valid:", result.IsValid()) 137 // Output: 138 // OneOf string valid: true 139 // AnyOf short string valid: false 140 } 141 142 func Example_conditionalSchema() { 143 // Conditional schema using if/then/else keywords 144 conditionalSchema := jsonschema.If( 145 jsonschema.Object( 146 jsonschema.Prop("type", jsonschema.Const("premium")), 147 ), 148 ).Then( 149 jsonschema.Object( 150 jsonschema.Prop("features", jsonschema.Array(jsonschema.MinItems(5))), 151 ), 152 ).Else( 153 jsonschema.Object( 154 jsonschema.Prop("features", jsonschema.Array(jsonschema.MaxItems(3))), 155 ), 156 ) 157 158 // Basic plan object 159 basicPlan := map[string]interface{}{ 160 "type": "basic", 161 "features": []interface{}{"feature1", "feature2"}, 162 } 163 164 result := conditionalSchema.Validate(basicPlan) 165 fmt.Println("Basic plan valid:", result.IsValid()) 166 // Output: Basic plan valid: true 167 } 168 169 func Example_convenienceFunctions() { 170 // Using convenience functions that apply format keywords 171 profileSchema := jsonschema.Object( 172 jsonschema.Prop("id", jsonschema.UUID()), 173 jsonschema.Prop("email", jsonschema.Email()), 174 jsonschema.Prop("website", jsonschema.URI()), 175 jsonschema.Prop("created", jsonschema.DateTime()), 176 jsonschema.Prop("score", jsonschema.PositiveInt()), 177 ) 178 179 data := map[string]interface{}{ 180 "id": "550e8400-e29b-41d4-a716-446655440000", 181 "email": "user@example.com", 182 "website": "https://example.com", 183 "created": "2023-01-01T00:00:00Z", 184 "score": 95, 185 } 186 187 result := profileSchema.Validate(data) 188 fmt.Println("Profile valid:", result.IsValid()) 189 // Output: Profile valid: true 190 } 191 192 func Example_compatibilityWithJSON() { 193 // New code construction approach 194 codeSchema := jsonschema.Object( 195 jsonschema.Prop("name", jsonschema.String()), 196 jsonschema.Prop("age", jsonschema.Integer()), 197 ) 198 199 // Existing JSON compilation approach 200 compiler := jsonschema.NewCompiler() 201 jsonSchema, err := compiler.Compile([]byte(`{ 202 "type": "object", 203 "properties": { 204 "name": {"type": "string"}, 205 "age": {"type": "integer"} 206 } 207 }`)) 208 if err != nil { 209 log.Fatal(err) 210 } 211 212 data := map[string]interface{}{ 213 "name": "Bob", 214 "age": 25, 215 } 216 217 // Both approaches work identically 218 result1 := codeSchema.Validate(data) 219 result2 := jsonSchema.Validate(data) 220 221 fmt.Println("Code schema valid:", result1.IsValid()) 222 fmt.Println("JSON schema valid:", result2.IsValid()) 223 // Output: 224 // Code schema valid: true 225 // JSON schema valid: true 226 } 227 228 func Example_schemaRegistration() { 229 // Create compiler for schema registration 230 compiler := jsonschema.NewCompiler() 231 232 // Create User schema with Constructor API 233 userSchema := jsonschema.Object( 234 jsonschema.ID("https://example.com/schemas/user"), 235 jsonschema.Prop("id", jsonschema.UUID()), 236 jsonschema.Prop("name", jsonschema.String(jsonschema.MinLen(1))), 237 jsonschema.Prop("email", jsonschema.Email()), 238 jsonschema.Required("id", "name", "email"), 239 ) 240 241 // Register the schema 242 compiler.SetSchema("https://example.com/schemas/user", userSchema) 243 244 // Create Profile schema that references User schema 245 profileJSON := `{ 246 "type": "object", 247 "properties": { 248 "user": {"$ref": "https://example.com/schemas/user"}, 249 "bio": {"type": "string"}, 250 "website": {"type": "string", "format": "uri"} 251 }, 252 "required": ["user"] 253 }` 254 255 profileSchema, err := compiler.Compile([]byte(profileJSON)) 256 if err != nil { 257 log.Fatal(err) 258 } 259 260 // Test with valid data 261 profileData := map[string]interface{}{ 262 "user": map[string]interface{}{ 263 "id": "550e8400-e29b-41d4-a716-446655440000", 264 "name": "Alice Johnson", 265 "email": "alice@example.com", 266 }, 267 "bio": "Software engineer", 268 "website": "https://alice.dev", 269 } 270 271 result := profileSchema.Validate(profileData) 272 fmt.Println("Profile with registered user schema valid:", result.IsValid()) 273 // Output: Profile with registered user schema valid: true 274 }