0
# Format Validation
1
2
Built-in format attribute validation for common data formats and extensible format validation system. Format validation provides semantic validation beyond basic JSON Schema types, ensuring data conforms to specific formats like email addresses, URIs, dates, and custom formats.
3
4
## Capabilities
5
6
### FormatAttribute Interface
7
8
Base interface for implementing format validation logic.
9
10
```java { .api }
11
/**
12
* Interface for implementing custom format validation
13
*/
14
public interface FormatAttribute {
15
/**
16
* Get supported JSON node types for this format
17
* @return EnumSet of NodeType values this format can validate
18
*/
19
EnumSet<NodeType> supportedTypes();
20
21
/**
22
* Validate format against instance data
23
* @param report ProcessingReport for collecting validation results
24
* @param bundle MessageBundle for error messages
25
* @param data FullData containing schema and instance information
26
* @throws ProcessingException if format validation processing fails
27
*/
28
void validate(ProcessingReport report, MessageBundle bundle, FullData data) throws ProcessingException;
29
}
30
31
/**
32
* Abstract base class providing common functionality for format attributes
33
*/
34
public abstract class AbstractFormatAttribute implements FormatAttribute {
35
/**
36
* Constructor with format name and supported types
37
* @param fmt Format name (e.g., "email", "date-time")
38
* @param first Required supported type
39
* @param other Additional supported types
40
*/
41
protected AbstractFormatAttribute(String fmt, NodeType first, NodeType... other);
42
43
/**
44
* Get format name
45
* @return String format name
46
*/
47
protected final String getFormatName();
48
49
/**
50
* Get supported node types
51
* @return EnumSet of supported NodeType values
52
*/
53
public final EnumSet<NodeType> supportedTypes();
54
55
/**
56
* Create new processing message for this format
57
* @param data Validation data
58
* @param bundle Message bundle
59
* @param key Message key
60
* @return ProcessingMessage for error reporting
61
*/
62
protected final ProcessingMessage newMsg(FullData data, MessageBundle bundle, String key);
63
}
64
```
65
66
### Built-in Format Attributes
67
68
#### Common Format Attributes
69
70
```java { .api }
71
/**
72
* Email format validation (RFC 5322)
73
*/
74
public final class EmailAttribute extends AbstractFormatAttribute {
75
public EmailAttribute();
76
}
77
78
/**
79
* URI format validation (RFC 3986)
80
*/
81
public final class URIAttribute extends AbstractFormatAttribute {
82
public URIAttribute();
83
}
84
85
/**
86
* Date-time format validation (RFC 3339)
87
*/
88
public final class DateTimeAttribute extends AbstractFormatAttribute {
89
public DateTimeAttribute();
90
}
91
92
/**
93
* Hostname format validation (RFC 1034)
94
*/
95
public final class HostnameAttribute extends AbstractFormatAttribute {
96
public HostnameAttribute();
97
}
98
99
/**
100
* IPv4 address format validation
101
*/
102
public final class IPv4Attribute extends AbstractFormatAttribute {
103
public IPv4Attribute();
104
}
105
106
/**
107
* IPv6 address format validation (RFC 4291)
108
*/
109
public final class IPv6Attribute extends AbstractFormatAttribute {
110
public IPv6Attribute();
111
}
112
113
/**
114
* Regular expression format validation
115
*/
116
public final class RegexAttribute extends AbstractFormatAttribute {
117
public RegexAttribute();
118
}
119
```
120
121
#### Draft v3 Specific Formats
122
123
```java { .api }
124
/**
125
* Date format validation (YYYY-MM-DD)
126
*/
127
public final class DateAttribute extends AbstractFormatAttribute {
128
public DateAttribute();
129
}
130
131
/**
132
* Time format validation (HH:MM:SS)
133
*/
134
public final class TimeAttribute extends AbstractFormatAttribute {
135
public TimeAttribute();
136
}
137
138
/**
139
* Phone number format validation
140
*/
141
public final class PhoneAttribute extends AbstractFormatAttribute {
142
public PhoneAttribute();
143
}
144
145
/**
146
* UTC milliseconds timestamp format validation
147
*/
148
public final class UTCMillisecAttribute extends AbstractFormatAttribute {
149
public UTCMillisecAttribute();
150
}
151
```
152
153
#### Extra Format Attributes
154
155
```java { .api }
156
/**
157
* Base64 encoded string format validation
158
*/
159
public final class Base64Attribute extends AbstractFormatAttribute {
160
public Base64Attribute();
161
}
162
163
/**
164
* Hexadecimal string format validation
165
*/
166
public final class HexStringAttribute extends AbstractFormatAttribute {
167
public HexStringAttribute();
168
}
169
170
/**
171
* UUID format validation (RFC 4122)
172
*/
173
public final class UUIDAttribute extends AbstractFormatAttribute {
174
public UUIDAttribute();
175
}
176
177
/**
178
* MD5 hash format validation
179
*/
180
public final class MD5Attribute extends AbstractFormatAttribute {
181
public MD5Attribute();
182
}
183
184
/**
185
* SHA1 hash format validation
186
*/
187
public final class SHA1Attribute extends AbstractFormatAttribute {
188
public SHA1Attribute();
189
}
190
191
/**
192
* SHA256 hash format validation
193
*/
194
public final class SHA256Attribute extends AbstractFormatAttribute {
195
public SHA256Attribute();
196
}
197
198
/**
199
* SHA512 hash format validation
200
*/
201
public final class SHA512Attribute extends AbstractFormatAttribute {
202
public SHA512Attribute();
203
}
204
```
205
206
### Format Dictionaries
207
208
Pre-built collections of format attributes for different JSON Schema versions.
209
210
```java { .api }
211
/**
212
* Common format attributes dictionary
213
*/
214
public final class CommonFormatAttributesDictionary {
215
/**
216
* Get dictionary of common format attributes
217
* @return Dictionary containing email, uri, datetime, hostname, ipv4, ipv6, regex formats
218
*/
219
public static Dictionary<FormatAttribute> get();
220
}
221
222
/**
223
* Draft v3 specific format attributes dictionary
224
*/
225
public final class DraftV3FormatAttributesDictionary {
226
/**
227
* Get dictionary of Draft v3 format attributes
228
* @return Dictionary containing date, time, phone, utc-millisec formats
229
*/
230
public static Dictionary<FormatAttribute> get();
231
}
232
233
/**
234
* Draft v4 format attributes dictionary
235
*/
236
public final class DraftV4FormatAttributesDictionary {
237
/**
238
* Get dictionary of Draft v4 format attributes
239
* @return Dictionary containing common formats for Draft v4
240
*/
241
public static Dictionary<FormatAttribute> get();
242
}
243
244
/**
245
* Extra format attributes dictionary
246
*/
247
public final class ExtraFormatsDictionary {
248
/**
249
* Get dictionary of additional format attributes
250
* @return Dictionary containing base64, hex, uuid, md5, sha1, sha256, sha512 formats
251
*/
252
public static Dictionary<FormatAttribute> get();
253
}
254
```
255
256
## Usage Examples
257
258
### Example 1: Using Built-in Format Validation
259
260
```java
261
import com.github.fge.jsonschema.cfg.ValidationConfiguration;
262
import com.github.fge.jsonschema.main.JsonSchemaFactory;
263
import com.fasterxml.jackson.databind.JsonNode;
264
import com.fasterxml.jackson.databind.ObjectMapper;
265
266
// Enable format validation (disabled by default for performance)
267
ValidationConfiguration config = ValidationConfiguration.newBuilder()
268
.setUseFormat(true)
269
.freeze();
270
271
JsonSchemaFactory factory = JsonSchemaFactory.newBuilder()
272
.setValidationConfiguration(config)
273
.freeze();
274
275
// Schema with format constraints
276
ObjectMapper mapper = new ObjectMapper();
277
JsonNode schema = mapper.readTree("{\n" +
278
" \"type\": \"object\",\n" +
279
" \"properties\": {\n" +
280
" \"email\": {\n" +
281
" \"type\": \"string\",\n" +
282
" \"format\": \"email\"\n" +
283
" },\n" +
284
" \"website\": {\n" +
285
" \"type\": \"string\",\n" +
286
" \"format\": \"uri\"\n" +
287
" },\n" +
288
" \"birthdate\": {\n" +
289
" \"type\": \"string\",\n" +
290
" \"format\": \"date-time\"\n" +
291
" },\n" +
292
" \"server\": {\n" +
293
" \"type\": \"string\",\n" +
294
" \"format\": \"hostname\"\n" +
295
" }\n" +
296
" }\n" +
297
"}");
298
299
JsonSchema jsonSchema = factory.getJsonSchema(schema);
300
301
// Valid instance
302
JsonNode validInstance = mapper.readTree("{\n" +
303
" \"email\": \"user@example.com\",\n" +
304
" \"website\": \"https://example.com\",\n" +
305
" \"birthdate\": \"1990-01-15T08:30:00Z\",\n" +
306
" \"server\": \"api.example.com\"\n" +
307
"}");
308
309
ProcessingReport validReport = jsonSchema.validate(validInstance);
310
System.out.println("Valid: " + validReport.isSuccess()); // true
311
312
// Invalid instance with format errors
313
JsonNode invalidInstance = mapper.readTree("{\n" +
314
" \"email\": \"invalid-email\",\n" +
315
" \"website\": \"not-a-uri\",\n" +
316
" \"birthdate\": \"invalid-date\",\n" +
317
" \"server\": \"invalid_hostname\"\n" +
318
"}");
319
320
ProcessingReport invalidReport = jsonSchema.validate(invalidInstance);
321
if (!invalidReport.isSuccess()) {
322
System.out.println("Format validation errors:");
323
for (ProcessingMessage message : invalidReport) {
324
System.out.println(" " + message.getMessage());
325
}
326
}
327
```
328
329
### Example 2: Custom Format Attribute
330
331
```java
332
import com.github.fge.jsonschema.format.AbstractFormatAttribute;
333
import com.github.fge.jsonschema.core.report.ProcessingReport;
334
import com.github.fge.jsonschema.processors.data.FullData;
335
336
/**
337
* Custom format attribute for validating ISBN numbers
338
*/
339
public final class ISBNFormatAttribute extends AbstractFormatAttribute {
340
private static final String FORMAT_NAME = "isbn";
341
342
public ISBNFormatAttribute() {
343
super(FORMAT_NAME, NodeType.STRING);
344
}
345
346
@Override
347
public void validate(ProcessingReport report, MessageBundle bundle,
348
FullData data) throws ProcessingException {
349
350
JsonNode instance = data.getInstance().getNode();
351
String isbn = instance.asText();
352
353
if (!isValidISBN(isbn)) {
354
ProcessingMessage message = newMsg(data, bundle, "err.format.isbn")
355
.putArgument("value", isbn);
356
report.error(message);
357
}
358
}
359
360
private boolean isValidISBN(String isbn) {
361
// Remove hyphens and spaces
362
String cleanISBN = isbn.replaceAll("[\\s-]", "");
363
364
// Check ISBN-10 or ISBN-13
365
return isValidISBN10(cleanISBN) || isValidISBN13(cleanISBN);
366
}
367
368
private boolean isValidISBN10(String isbn) {
369
if (isbn.length() != 10) return false;
370
371
int sum = 0;
372
for (int i = 0; i < 9; i++) {
373
if (!Character.isDigit(isbn.charAt(i))) return false;
374
sum += (isbn.charAt(i) - '0') * (10 - i);
375
}
376
377
char checkChar = isbn.charAt(9);
378
int checkDigit = (checkChar == 'X') ? 10 : Character.getNumericValue(checkChar);
379
380
return (sum + checkDigit) % 11 == 0;
381
}
382
383
private boolean isValidISBN13(String isbn) {
384
if (isbn.length() != 13) return false;
385
386
int sum = 0;
387
for (int i = 0; i < 12; i++) {
388
if (!Character.isDigit(isbn.charAt(i))) return false;
389
int digit = isbn.charAt(i) - '0';
390
sum += (i % 2 == 0) ? digit : digit * 3;
391
}
392
393
int checkDigit = Character.getNumericValue(isbn.charAt(12));
394
return (sum + checkDigit) % 10 == 0;
395
}
396
}
397
398
// Register custom format attribute
399
Library customLibrary = DraftV4Library.get().thaw()
400
.addFormatAttribute("isbn", new ISBNFormatAttribute())
401
.freeze();
402
403
ValidationConfiguration config = ValidationConfiguration.newBuilder()
404
.addLibrary("http://json-schema.org/draft-04/schema#", customLibrary)
405
.setUseFormat(true)
406
.freeze();
407
408
JsonSchemaFactory factory = JsonSchemaFactory.newBuilder()
409
.setValidationConfiguration(config)
410
.freeze();
411
412
// Use custom format in schema
413
String schemaWithISBN = "{\n" +
414
" \"type\": \"object\",\n" +
415
" \"properties\": {\n" +
416
" \"book_isbn\": {\n" +
417
" \"type\": \"string\",\n" +
418
" \"format\": \"isbn\"\n" +
419
" }\n" +
420
" }\n" +
421
"}";
422
```
423
424
### Example 3: Format Validation Service
425
426
```java
427
/**
428
* Service for format validation with multiple format libraries
429
*/
430
public class FormatValidationService {
431
private final JsonSchemaFactory standardFactory;
432
private final JsonSchemaFactory extendedFactory;
433
434
public FormatValidationService() {
435
// Standard factory with built-in formats
436
ValidationConfiguration standardConfig = ValidationConfiguration.newBuilder()
437
.setUseFormat(true)
438
.freeze();
439
this.standardFactory = JsonSchemaFactory.newBuilder()
440
.setValidationConfiguration(standardConfig)
441
.freeze();
442
443
// Extended factory with additional formats
444
Library extendedLibrary = DraftV4Library.get().thaw()
445
.addFormatAttribute("isbn", new ISBNFormatAttribute())
446
.addFormatAttribute("credit-card", new CreditCardFormatAttribute())
447
.addFormatAttribute("iban", new IBANFormatAttribute())
448
.freeze();
449
450
ValidationConfiguration extendedConfig = ValidationConfiguration.newBuilder()
451
.addLibrary("http://json-schema.org/draft-04/schema#", extendedLibrary)
452
.setUseFormat(true)
453
.freeze();
454
455
this.extendedFactory = JsonSchemaFactory.newBuilder()
456
.setValidationConfiguration(extendedConfig)
457
.freeze();
458
}
459
460
public ProcessingReport validateWithStandardFormats(JsonNode schema, JsonNode instance) throws ProcessingException {
461
JsonSchema jsonSchema = standardFactory.getJsonSchema(schema);
462
return jsonSchema.validate(instance);
463
}
464
465
public ProcessingReport validateWithExtendedFormats(JsonNode schema, JsonNode instance) throws ProcessingException {
466
JsonSchema jsonSchema = extendedFactory.getJsonSchema(schema);
467
return jsonSchema.validate(instance);
468
}
469
470
public List<String> getSupportedFormats(boolean includeExtended) {
471
List<String> formats = Arrays.asList(
472
"email", "uri", "date-time", "hostname", "ipv4", "ipv6", "regex"
473
);
474
475
if (includeExtended) {
476
List<String> extended = new ArrayList<>(formats);
477
extended.addAll(Arrays.asList("isbn", "credit-card", "iban", "base64", "uuid"));
478
return extended;
479
}
480
481
return formats;
482
}
483
}
484
```
485
486
### Example 4: Conditional Format Validation
487
488
```java
489
/**
490
* Format validator with conditional validation logic
491
*/
492
public final class ConditionalEmailAttribute extends AbstractFormatAttribute {
493
public ConditionalEmailAttribute() {
494
super("conditional-email", NodeType.STRING);
495
}
496
497
@Override
498
public void validate(ProcessingReport report, MessageBundle bundle,
499
FullData data) throws ProcessingException {
500
501
JsonNode instance = data.getInstance().getNode();
502
JsonNode schema = data.getSchema().getNode();
503
String email = instance.asText();
504
505
// Check if strict validation is enabled in schema
506
boolean strictMode = schema.path("strictEmail").asBoolean(false);
507
508
if (strictMode) {
509
validateStrictEmail(email, report, bundle, data);
510
} else {
511
validateBasicEmail(email, report, bundle, data);
512
}
513
}
514
515
private void validateStrictEmail(String email, ProcessingReport report,
516
MessageBundle bundle, FullData data) throws ProcessingException {
517
// Strict RFC 5322 validation
518
if (!email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")) {
519
ProcessingMessage message = newMsg(data, bundle, "err.format.email.strict")
520
.putArgument("value", email);
521
report.error(message);
522
}
523
}
524
525
private void validateBasicEmail(String email, ProcessingReport report,
526
MessageBundle bundle, FullData data) throws ProcessingException {
527
// Basic email validation
528
if (!email.contains("@") || !email.contains(".")) {
529
ProcessingMessage message = newMsg(data, bundle, "err.format.email.basic")
530
.putArgument("value", email);
531
report.error(message);
532
}
533
}
534
}
535
536
// Usage in schema
537
String conditionalSchema = "{\n" +
538
" \"type\": \"object\",\n" +
539
" \"properties\": {\n" +
540
" \"email\": {\n" +
541
" \"type\": \"string\",\n" +
542
" \"format\": \"conditional-email\",\n" +
543
" \"strictEmail\": true\n" +
544
" }\n" +
545
" }\n" +
546
"}";
547
```
548
549
## Available Format Attributes
550
551
### Common Formats (always available)
552
- `email` - Email addresses (RFC 5322)
553
- `uri` - Uniform Resource Identifiers (RFC 3986)
554
- `date-time` - Date and time (RFC 3339)
555
- `hostname` - Internet hostnames (RFC 1034)
556
- `ipv4` - IPv4 addresses
557
- `ipv6` - IPv6 addresses (RFC 4291)
558
- `regex` - Regular expressions
559
560
### Draft v3 Specific Formats
561
- `date` - Date in YYYY-MM-DD format
562
- `time` - Time in HH:MM:SS format
563
- `phone` - Phone number validation
564
- `utc-millisec` - UTC milliseconds timestamp
565
566
### Extra Formats (available via ExtraFormatsDictionary)
567
- `base64` - Base64 encoded strings
568
- `hex` - Hexadecimal strings
569
- `uuid` - UUID strings (RFC 4122)
570
- `md5` - MD5 hash strings
571
- `sha1` - SHA1 hash strings
572
- `sha256` - SHA256 hash strings
573
- `sha512` - SHA512 hash strings
574
575
## Best Practices
576
577
1. **Performance**: Enable format validation only when needed (`setUseFormat(true)`)
578
2. **Extensibility**: Create custom formats for domain-specific validation requirements
579
3. **Error Messages**: Provide clear, actionable error messages in custom formats
580
4. **Type Safety**: Use appropriate NodeType constraints in format attributes
581
5. **Validation Logic**: Keep format validation focused and efficient
582
6. **Testing**: Thoroughly test custom formats with valid and invalid inputs
583
7. **Standards Compliance**: Follow relevant RFCs and standards for format validation