java
56 lines · 8 steps
Validating JSON payloads against a schema in Java
A reusable validator loads a JSON Schema once, then reports every violation as a flat list of readable messages.
Explained by
highlit
1package com.example.validation;
2
3import java.util.List;
4import java.util.stream.Collectors;
5
6import org.everit.json.schema.Schema;
7import org.everit.json.schema.ValidationException;
8import org.everit.json.schema.loader.SchemaLoader;
9import org.json.JSONObject;
10import org.json.JSONTokener;
11
12public class PayloadValidator {
13
14 private final Schema schema;
15
16 public PayloadValidator(String schemaResourcePath) {
17 try (var stream = getClass().getResourceAsStream(schemaResourcePath)) {
18 if (stream == null) {
19 throw new IllegalStateException("Schema not found: " + schemaResourcePath);
20 }
21 JSONObject rawSchema = new JSONObject(new JSONTokener(stream));
22 this.schema = SchemaLoader.builder()
23 .schemaJson(rawSchema)
24 .draftV7Support()
25 .build()
26 .load()
27 .build();
28 } catch (Exception e) {
29 throw new IllegalStateException("Failed to load schema", e);
30 }
31 }
32
33 public List<String> validate(String json) {
34 JSONObject payload;
35 try {
36 payload = new JSONObject(new JSONTokener(json));
37 } catch (org.json.JSONException e) {
38 return List.of("Malformed JSON: " + e.getMessage());
39 }
40 try {
41 schema.validate(payload);
42 return List.of();
43 } catch (ValidationException e) {
44 return flatten(e);
45 }
46 }
47
48 private List<String> flatten(ValidationException e) {
49 if (e.getCausingExceptions().isEmpty()) {
50 return List.of(e.getPointerToViolation() + ": " + e.getErrorMessage());
51 }
52 return e.getCausingExceptions().stream()
53 .flatMap(cause -> flatten(cause).stream())
54 .collect(Collectors.toList());
55 }
56}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Compile expensive resources like schemas once in the constructor so each validation call stays cheap.
- 2Returning an empty-or-populated list of errors is friendlier to callers than throwing on the first failure.
- 3Recursively walking a nested exception's causes turns a tree of violations into one flat, actionable report.
Related explainers
java
public final class NaturalOrderComparator implements Comparator<String> { public static final NaturalOrderComparator INSTANCE = new NaturalOrderComparator();
Natural-order string sorting in Java
comparator
natural-sort
string-parsing
Intermediate
9 steps
go
package middleware import ( "net/http"
Localized validation errors in Gin
middleware
internationalization
validation
Intermediate
8 steps
javascript
async function uploadInBatches(records, uploadFn, { batchSize = 100, concurrency = 3 } = {}) { const batches = []; for (let i = 0; i < records.length; i += batchSize) { batches.push(records.slice(i, i + batchSize));
Uploading records with bounded concurrency
concurrency
worker-pool
async-await
Advanced
8 steps
python
from flask import Blueprint, jsonify from marshmallow import Schema, fields, validate, EXCLUDE from webargs.flaskparser import use_args
Validating query params in Flask with webargs
validation
schema
query-building
Intermediate
10 steps
java
public URI buildSearchUri(String query, int page, int size, List<String> tags) { UriComponentsBuilder builder = UriComponentsBuilder .fromUriString("https://api.example.com") .path("/v2/products/search")
Building URIs safely with UriComponentsBuilder in Spring
url-building
builder-pattern
encoding
Intermediate
5 steps
go
func (h *ExportHandler) BulkExport(c *gin.Context) { projectID := c.Param("projectID") reports, err := h.reports.ListByProject(c.Request.Context(), projectID)
Streaming a ZIP download in Gin
streaming
zip-archive
http-headers
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/validating-json-payloads-against-a-schema-in-java-explained-java-4afa/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.