CodeGym /Kursy /JAVA 25 SELF /Walidacja JSON: JSON Schema, błędy walidacji

Walidacja JSON: JSON Schema, błędy walidacji

JAVA 25 SELF
Poziom 46 , Lekcja 4
Dostępny

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.

1
Ankieta/quiz
Serializacja JSON, poziom 46, lekcja 4
Niedostępny
Serializacja JSON
Serializacja JSON
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION