CodeGym /Corsi /JAVA 25 SELF /Convalida JSON: JSON Schema, errori di convalida

Convalida JSON: JSON Schema, errori di convalida

JAVA 25 SELF
Livello 46, Lezione 4
Disponibile

1. Perché serve la convalida JSON

Immaginiamo: avete scritto la classe User e vi aspettate che in ingresso arrivi sempre un JSON di questo tipo:

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com"
}

Ma all’improvviso vi arriva questo:

{
  "id": "quarantadue",
  "name": 123,
  "email": null,
  "admin": true
}

O addirittura:

{
  "username": "Alice"
}

Nel migliore dei casi Jackson o Gson lanceranno un’eccezione durante il tentativo di deserializzazione. Nel peggiore — assegneranno silenziosamente ai campi i valori predefiniti, e il vostro codice di business inizierà a comportarsi in modo scorretto. E se si tratta della configurazione del vostro servizio — potreste imbattervi in bug «divertenti», che poi tutta la squadra dovrà cercare.

La convalida JSON è il processo di verifica che struttura, tipi e valori dei dati in JSON rispettino regole definite (uno schema). È come il controllo passaporti per i dati: se non lo superi — non si sale a bordo!

2. JSON Schema: che cos’è e come si presenta

Nel mondo JSON esiste uno standard ufficiale per descrivere la struttura dei dati — JSON Schema. È una sorta di «lista di controllo», con cui verificare che un JSON soddisfi i requisiti della vostra applicazione.

JSON Schema è anch’esso JSON, solo che con chiavi speciali: type, properties, required e così via.

Esempio di schema molto semplice

{
  "$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"]
}

Che cosa succede qui:

  • Ci si aspetta un oggetto (type: "object").
  • L’oggetto può avere i campi "id", "name", "email" (descritti in properties).
  • "id" — è obbligatoriamente un intero (type: "integer").
  • "name" — è obbligatoriamente una stringa (type: "string").
  • "email" — è una stringa che ha l’aspetto di un’email (chiave format con valore "email").
  • required indica l’elenco dei campi obbligatori: "id" e "name".

Se nel JSON mancano "id" o "name", oppure il loro tipo non è quello previsto — la convalida non passerà.

Panoramica rapida delle funzionalità di JSON Schema

  • Indicazione del tipo (type: "string", "integer", "array", "object", "boolean", "null").
  • Descrizione di oggetti e array annidati (properties, items).
  • Campi obbligatori e facoltativi (required).
  • Verifica della lunghezza delle stringhe, dell’intervallo numerico (minLength, maximum e altro).
  • Verifica del formato (format: "email", "date", "uri", ecc.).
  • Elenchi di valori consentiti (enum: insieme di valori ammessi).
  • Espressioni regolari per le stringhe (pattern).
  • Condizioni complesse: anyOf, oneOf, allOf (per casi avanzati).

3. Convalida JSON in Java: panoramica delle librerie

La libreria standard di Java non include la convalida JSON basata su schema. Esistono però librerie di terze parti popolari. Ecco le più note:

  • everit-org/json-schema — semplice, gratuita e popolare.
  • networknt/json-schema-validator — veloce, supporta gli standard più recenti.
  • Jackson-module-jsonSchema — estensione per Jackson (ma non supporta una convalida completa).
  • Justify, Java JSON Tools — ce ne sono anche altre, ma sono meno comuni.

In questa lezione prenderemo in esame everit-org/json-schema — è semplice per i principianti, ben documentata e non richiede acrobazie.

Installazione di everit-org/json-schema

Aggiungete la dipendenza al vostro pom.xml (Maven):

<dependency>
  <groupId>org.everit.json</groupId>
  <artifactId>org.everit.json.schema</artifactId>
  <version>1.14.2</version>
</dependency>

Oppure tramite Gradle:

implementation 'org.everit.json:org.everit.json.schema:1.14.2'

4. Esempio: convalida JSON basata su schema (passo dopo passo)

Proviamo a convalidare un JSON nella pratica. Ci serviranno lo schema, il JSON e un po’ di codice.

Esempio di schema (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"]
}

Esempio di JSON valido (user.json):

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com"
}

Esempio di JSON non valido:

{
  "id": "uno",
  "name": "",
  "email": "not-an-email"
}

Codice per la convalida

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 {
        // Caricamento dello schema dal file
        String schemaString = new String(Files.readAllBytes(Paths.get("user-schema.json")));
        JSONObject rawSchema = new JSONObject(new JSONTokener(schemaString));
        Schema schema = SchemaLoader.load(rawSchema);

        // Caricamento del JSON da verificare
        String jsonString = new String(Files.readAllBytes(Paths.get("user.json")));
        JSONObject json = new JSONObject(new JSONTokener(jsonString));

        // Convalida
        try {
            schema.validate(json); // Se va tutto bene — non succederà nulla
            System.out.println("JSON valido!");
        } catch (ValidationException e) {
            System.out.println("JSON NON valido!");
            for (String msg : e.getAllMessages()) {
                System.out.println("Errore: " + msg);
            }
        }
    }
}

Commenti al codice:

  • Si usa la classe Schema e il loader SchemaLoader.load(...).
  • Il metodo schema.validate(json) lancia un’eccezione in caso di non conformità allo schema.
  • Nel blocco catch è possibile ottenere tutti gli errori tramite getAllMessages().

Come integrarlo in un’applicazione?

Di solito lo schema è conservato tra le risorse (ad esempio nella cartella resources). Si convalida il JSON prima della deserializzazione in un oggetto Java. Se tutto è a posto — si deserializza e si procede.

5. Gestione degli errori di convalida

Quando un JSON non supera il controllo, la libreria lancia l’eccezione ValidationException. Nel messaggio è presente un elenco di errori: che cosa esattamente non è conforme allo schema.

Esempio di output degli errori

Per il JSON non valido di cui sopra, l’output sarà circa questo:

JSON NON valido!
Errore: #: required key [id] not found
Errore: #/name: expected minLength: 2, actual: 0
Errore: #/email: String [not-an-email] is invalid against requested format [email]
Errore: #/id: expected type: Integer, found: String

Come interpretare gli errori:

  • required key [id] not found — manca un campo obbligatorio.
  • expected minLength: 2, actual: 0 — la stringa è troppo corta.
  • String [...] is invalid against requested format [email] — email non valida.
  • expected type: Integer, found: String — il tipo non corrisponde.

Importante! I messaggi possono essere in inglese, ma sono molto comprensibili.

Come mostrare gli errori all’utente?

Potete raccogliere gli errori in un elenco e restituirli all’utente, ad esempio in un REST API o in una GUI. Questo permette di capire rapidamente che cosa non va nei dati in ingresso.

6. Pratica: convalida di un array di oggetti

Spesso bisogna convalidare non un singolo oggetto, ma un array:

[
  { "id": 1, "name": "Alice", "email": "alice@example.com" },
  { "id": 2, "name": "Bob" },
  { "id": "che cos'è?", "name": 123, "email": "not-an-email" }
]

Schema:

{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id":    { "type": "integer" },
      "name":  { "type": "string", "minLength": 2, "maxLength": 30 },
      "email": { "type": "string", "format": "email" }
    },
    "required": ["id", "name"]
  }
}

La convalida funziona allo stesso modo, solo che come JSON si passa un array. Gli errori conterranno gli indici degli elementi in cui qualcosa non va (per esempio, [#/2/id]).

7. Errori tipici nella convalida JSON

Errore n. 1: tipi di dati non corrispondenti. Molto spesso arriva una stringa al posto di un numero ("id": "123"), mentre lo schema si aspetta un integer. La convalida non passerà. Se non controllate la fonte dei dati — correggete lo schema oppure convertite i dati in anticipo.

Errore n. 2: campi obbligatori mancanti. Se nello schema è indicato che un campo è obbligatorio ("required": ["id","name"]), ma nel JSON manca — otterrete un errore. Talvolta accade inaspettatamente: il frontend ha dimenticato di inviare il campo o l’API è cambiata.

Errore n. 3: campi in più nel JSON. Per impostazione predefinita JSON Schema consente campi extra. Se volete uno schema rigoroso, non dimenticate di aggiungere "additionalProperties": false. Senza questo, nel JSON possono esserci campi «non previsti».

Errore n. 4: versione dello schema o sintassi errata. Se usate chiavi non presenti nella versione dello schema in uso, o fate errori di battitura, il validatore non potrà caricare lo schema. Verificate lo schema su https://www.jsonschemavalidator.net/ o servizi analoghi.

Errore n. 5: gestione degli errori scadente. Se vi limitate a catturare la prima eccezione e non mostrate all’utente i dettagli, sarà difficile capire che cosa non va nel JSON. Usate getAllMessages() per mostrare tutti gli errori.

Errore n. 6: formati troppo rigidi. La verifica di "format": "email" o "date" può essere piuttosto «superficiale». Se avete bisogno di una convalida rigorosa, aggiungete controlli supplementari nel codice.

1
Compito
JAVA 25 SELF, livello 46, lezione 4
Bloccato
Ispezione dei nuovi profili utente 🧐
Ispezione dei nuovi profili utente 🧐
1
Compito
JAVA 25 SELF, livello 46, lezione 4
Bloccato
Il guardiano all'ingresso di un club online: verifica dell'email 🚫
Il guardiano all'ingresso di un club online: verifica dell'email 🚫
1
Sondaggio/quiz
Serializzazione JSON, livello 46, lezione 4
Non disponibile
Serializzazione JSON
Serializzazione JSON
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION