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
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
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.