1. Po co jest walidacja JSON
Wyobraźmy sobie: napisaliście klasę User i oczekujecie, że na wejściu zawsze będzie przychodził taki JSON:
{
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
Ale nagle przychodzi coś takiego:
{
"id": "czterdzieści dwa",
"name": 123,
"email": null,
"admin": true
}
Albo wręcz:
{
"username": "Alice"
}
W najlepszym wypadku Jackson lub Gson wyrzucą wyjątek przy próbie deserializacji. W najgorszym — po cichu przypiszą polom wartości domyślne, i wasz kod biznesowy zacznie działać niepoprawnie. A jeśli to konfiguracja waszej usługi — można wpaść na „wesołe” bugi, których potem szuka cała drużyna.
Walidacja JSON — to proces sprawdzania, że struktura, typy i wartości danych w JSON odpowiadają określonym regułom (schemie). To jak kontrola paszportowa dla danych: nie przeszedł — nie wpuszczamy na pokład!
2. JSON Schema: co to jest i jak wygląda
W świecie JSON istnieje oficjalny standard opisu struktury danych — JSON Schema. To taka „lista kontrolna”, według której można sprawdzić, czy JSON spełnia wymagania waszego programu.
JSON Schema to również JSON, tylko ze specjalnymi kluczami: type, properties, required i tak dalej.
Przykład najprostszego schematu
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
Co się tu dzieje:
- Oczekiwany jest obiekt (type: "object").
- Obiekt może mieć pola "id", "name", "email" (opisane w properties).
- "id" — obowiązkowo liczba całkowita (type: "integer").
- "name" — obowiązkowo łańcuch znaków (type: "string").
- "email" — łańcuch, który wygląda jak e‑mail (klucz format o wartości "email").
- required wskazuje listę pól wymaganych: "id" i "name".
Jeśli w JSON zabraknie "id" lub "name", albo ich typ będzie inny — walidacja nie przejdzie.
Krótko o możliwościach JSON Schema
- Określenie typu (type: "string", "integer", "array", "object", "boolean", "null").
- Opis zagnieżdżonych obiektów i tablic (properties, items).
- Pola wymagane i opcjonalne (required).
- Sprawdzanie długości łańcuchów, zakresu liczb (minLength, maximum itd.).
- Sprawdzanie formatu (format: "email", "date", "uri" itd.).
- Wyliczenia (enum: lista dopuszczalnych wartości).
- Wyrażenia regularne dla łańcuchów (pattern).
- Złożone warunki: anyOf, oneOf, allOf (dla zaawansowanych przypadków).
3. Walidacja JSON w Javie: przegląd bibliotek
Standardowa biblioteka Javy nie zawiera walidacji JSON według schematu. Istnieją jednak popularne biblioteki zewnętrzne. Oto najbardziej znane:
- everit-org/json-schema — prosta, darmowa i popularna.
- networknt/json-schema-validator — szybka, wspiera najnowsze standardy.
- Jackson-module-jsonSchema — rozszerzenie dla Jacksona (ale nie wspiera pełnej walidacji).
- Justify, Java JSON Tools — są też inne, ale spotyka się je rzadziej.
W tym wykładzie przyjrzymy się everit-org/json-schema — jest prosta dla początkujących, dobrze udokumentowana i nie wymaga żadnych „kombinacji”.
Instalacja everit-org/json-schema
Dodaj zależność do swojego pom.xml (Maven):
<dependency>
<groupId>org.everit.json</groupId>
<artifactId>org.everit.json.schema</artifactId>
<version>1.14.2</version>
</dependency>
Albo przez Gradle:
implementation 'org.everit.json:org.everit.json.schema:1.14.2'
4. Przykład: walidacja JSON według schematu (krok po kroku)
Spróbujmy zwalidować JSON w praktyce. Potrzebny będzie sam schemat, JSON i odrobina kodu.
Przykład schematu (user-schema.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
Przykład prawidłowego JSON (user.json):
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
Przykład nieprawidłowego JSON:
{
"id": "jeden",
"name": "",
"email": "not-an-email"
}
Kod do walidacji
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONTokener;
import org.everit.json.schema.ValidationException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class JsonValidationExample {
public static void main(String[] args) throws Exception {
// Wczytanie schematu z pliku
String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
Schema schema = SchemaLoader.load(rawSchema);
// Wczytanie JSON do sprawdzenia
String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
JSONObject json = new JSONObject(new JSONTokener(jsonString));
// Walidacja
try {
schema.validate(json); // Jeśli wszystko jest OK — nic się nie wydarzy
System.out.println("JSON jest poprawny!");
} catch (ValidationException e) {
System.out.println("JSON NIE jest poprawny!");
for (String msg : e.getAllMessages()) {
System.out.println("Błąd: " + msg);
}
}
}
}
Komentarze do kodu:
- Wykorzystujemy klasę Schema oraz loader SchemaLoader.load(...).
- Metoda schema.validate(json) rzuca wyjątek przy niezgodności ze schematem.
- W bloku catch można pobrać wszystkie błędy przez getAllMessages().
Jak to zintegrować z aplikacją?
Zwykle schemat trzymamy w zasobach (np. w katalogu resources). Walidujesz JSON przed deserializacją do obiektu Javy. Jeśli wszystko jest dobrze — deserializujesz i działasz dalej.
5. Obsługa błędów walidacji
Gdy JSON nie przechodzi weryfikacji, biblioteka rzuca wyjątek ValidationException. W komunikacie znajduje się lista błędów: co dokładnie nie odpowiada schematowi.
Przykładowy wynik błędów
Dla nieprawidłowego JSON powyżej wynik będzie mniej więcej taki:
JSON NIE jest poprawny!
Błąd: #: required key [id] not found
Błąd: #/name: expected minLength: 2, actual: 0
Błąd: #/email: String [not-an-email] is invalid against requested format [email]
Błąd: #/id: expected type: Integer, found: String
Jak interpretować błędy:
- required key [id] not found — brakuje wymaganego pola.
- expected minLength: 2, actual: 0 — łańcuch jest zbyt krótki.
- String [...] is invalid against requested format [email] — nieprawidłowy e‑mail.
- expected type: Integer, found: String — typ się nie zgadza.
Ważne! Komunikaty mogą być po angielsku, ale są bardzo zrozumiałe.
Jak pokazać błędy użytkownikowi?
Możesz zebrać błędy do listy i zwrócić użytkownikowi, na przykład w REST API lub GUI. Pozwoli to szybko zrozumieć, co jest nie tak z danymi wejściowymi.
6. Praktyka: walidacja tablicy obiektów
Często trzeba walidować nie jeden obiekt, lecz tablicę:
[
{ "id": 1, "name": "Alice", "email": "alice@example.com" },
{ "id": 2, "name": "Bob" },
{ "id": "co to jest?", "name": 123, "email": "not-an-email" }
]
Schemat:
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 2, "maxLength": 30 },
"email": { "type": "string", "format": "email" }
},
"required": ["id", "name"]
}
}
Walidacja działa tak samo, tylko jako JSON przekazujemy tablicę. Błędy będą zawierać indeksy elementów, w których coś jest nie tak (na przykład [#/2/id]).
7. Typowe błędy przy walidacji JSON
Błąd nr 1: Niezgodność typów danych. Bardzo często przychodzi łańcuch zamiast liczby ("id": "123"), a schemat oczekuje integer. Walidacja nie przejdzie. Jeśli nie kontrolujesz źródła danych — albo popraw schemat, albo wcześniej przekonwertuj dane.
Błąd nr 2: Brak wymaganych pól. Jeśli w schemacie wskazano, że pole jest wymagane ("required": ["id","name"]), a w JSON go nie ma — pojawi się błąd. Czasem bywa to zaskoczeniem: frontend zapomniał wysłać pole lub API się zmieniło.
Błąd nr 3: Nadmiarowe pola w JSON. Domyślnie JSON Schema dopuszcza dodatkowe pola. Jeśli chcesz ścisłego schematu, nie zapomnij dodać "additionalProperties": false. Bez tego w JSON mogą występować dowolne „obce” pola.
Błąd nr 4: Nieprawidłowa wersja schematu lub składnia. Jeśli używasz kluczy, których nie ma w używanej wersji schematu, albo robisz literówki, walidator nie zdoła wczytać schematu. Sprawdzaj schemat na https://www.jsonschemavalidator.net/ lub podobnych serwisach.
Błąd nr 5: Słaba obsługa błędów. Jeśli łapiesz tylko pierwszy wyjątek i nie pokazujesz użytkownikowi szczegółów, trudno będzie zrozumieć, co dokładnie jest nie tak z JSON. Używaj getAllMessages() do wypisania wszystkich błędów.
Błąd nr 6: Zbyt restrykcyjne formaty. Sprawdzanie "format": "email" lub "date" bywa dość „powierzchowne”. Jeśli potrzebujesz ścisłej walidacji, użyj dodatkowych weryfikacji w kodzie.
GO TO FULL VERSION