Sono consapevole che documentare e validare i dati anziché la sorgente che li genera potrebbe non essere l’approccio migliore, di recente mi è però capitato di dover mettere in piedi un backend “estremamente lean” (eufemismo per dire che è tutto basato su un file json), e generare un json-schema per verificarne la correttezza mi è sembrato doveroso…
Ma facciamo un passo indietro: l’applicazione mobile “backend-free”
A tutti quelli che fanno questo mestiere prima o poi vengono idee su possibili applicazioni web/mobile da sviluppare per esigenze personali, come progetto di studio o ancora meglio perché vogliamo vedere se la cosa può prendere campo, ma il tempo libero è poco e lo sviluppo di front-end e back-end può significare una mole di lavoro tale da farci lasciar perdere. Ci sono però casi in cui i dati sono pochi e non troppo preziosi, al che mettere inizialmente un file json su una cartella web può essere sufficiente.
Una soluzione che mi è sembrata molto comoda almeno nella fase di “sondaggio del mercato” è quella di copiare il/i file json su una cartella Dropbox, copiarne il link e utilizzare quello come url del nostro “servizio web dei poveri”. Nota: se utilizzate Dropbox è necessario che l’url sia nella forma https://dl.dropbox.com/...
, se al posto del “dl” lasciate “www” al vostro client arriva non il file json ma la pagina html di visualizzazione di Dropbox.
Se Dropbox non vi piace di alternative simili ce ne sono tante, oppure se avete uno spazio web potete usare quello, a me però piace Dropbox anche perché non ho limiti di traffico, posso gestire i dati in vari modi, e il rischio che il server “vada giù” è praticamente nullo.
Detto che una soluzione del genere può andar bene soltanto se l’idea che qualcuno vi copi tutti i dati non vi spaventa, la scomodità maggiore è la totale mancanza di qualcosa che vi faciliti nell’inserimento e la manutenzione dei dati, cosa che può portare a inserire dati in un formato non corretto con inevitabili malfunzionamenti sulla vostra applicazione client. Ecco quindi che entra in scena il json-schema.
Il json-schema, sempre di json si tratta
Iniziamo con il dire che così come l’xml-schema è un xml, il json-schema è un json, e allo stesso modo compilarlo è una scocciatura. Il sito web di riferimento per i json-schema è json-schema.org, sito bruttarello ma pieno di risorse.
Sebbene l’idea di scrivere prima o poi uno schema ce l’avessi avuta fin dall’inizio, mi sono ritrovato a farlo soltanto quando la struttura dati era più o meno formata (seppur con dati mock). Mettersi a scrivere lo schema tenendo di fianco i dati è abbastanza noioso e non è nemmeno facile, infatti sono arrivato a un punto che il linter mi segnalava lo schema come non valido e non sapevo il perché.
Alla fine l’approccio che ho seguito è stato il seguente:
- scrittura di un json con almeno qualche elemento completo, così da avere dei dati con tutti i casi particolari
- generazione dello schema utilizzando un servizio come jsonschema.net; l’importante è che venga fuori una struttura “abbastanza aderente” a quello che avete in mente
- pulizia dello schema dagli artefatti inutili e copia in un linter come jsonschemalint.com insieme ai dati
- rifinitura dei dettagli relativi ai tipi, gestioni dei valori null, eventuali regex sulle stringhe; descrizioni sui campi meno chiari
- eventuali modifiche ai dati per rendere la struttura più uniforme
- se serve che qualcuno poco familiare con il formato json “capisca” i dati può essere utile generarsi una documentazione a partire dallo schema, Docson è semplice da usare e da un buon risultato
Qualche dritta sui json-schema
A questo punto è doveroso un esempio riepilogativo con quasi tutte le funzionalità che sono servite a me, così almeno sapete a cosa andrete incontro…
{ "data": "2015-12-03T00:00:00", "lista": [ { "id": 1, "nome": "Articolo 1", "email": null, "tipo": "tipo1", "classe": 1, "flag": true, "figli": [ { "data": "2015-08-16", "note": "blablabla" } ] } ] }
{ "$schema": "http://json-schema.org/draft-04/schema#", "description": "Esempio", "type": "object", "properties": { "data": { "description": "Data con formato yyyy-MM-ddTHH:mm:ss", "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$" }, "lista": { "description": "Lista di oggetti", "type": "array", "items": { "type": "object", "properties": { "id": { "description": "Identificatore univoco", "type": "integer" }, "nome": { "type": "string" }, "email": { "type": ["string", "null"], "format": "email" }, "tipo": { "description": "Tipologia a scelta tra tre valori", "type": "string", "enum": ["tipo1", "tipo2", "tipo3"] }, "classe": { "description": "Classe con valore numerico da 1 a 5", "type": "integer", "minimum": 1, "maximum": 4, "exclusiveMinimum": false, "exclusiveMaximum": false }, "flag": { "description": "Flag booleano", "type": "boolean" }, "figli": { "description": "Elenco di oggetti figli", "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "data": { "description": "Data con formato classico yyyy-MM-dd", "type": "string", "format": "date" }, "note": { "type": ["string", "null"] } }, "additionalProperties": false, "required": ["data"] }, "additionalItems": false } }, "additionalProperties": false, "required": ["id", "nome", "tipo"] }, "additionalItems": false } }, "additionalProperties": false, "required": ["data"] }
Un esempio abbastanza ricco, e che fa capire quanto uno schema possa venire “corposo” anche quando si riferisce a un json quasi insignificante.
- campo data di primo livello: classica regex definita con l’attributo
pattern
, in cui però non si possono usare i classici segnaposto \d, \s - campo lista di primo livello: array di oggetti, attenzione perché questo è il modo in cui si gestiscono “oggetti uniformi” (caso tipico secondo me), si possono gestire anche oggetti diversi ciascuno in una posizione dell’array
- campo email di secondo livello: stringa ma che accetta anche valori null, il formato dell’email viene validato con l’attributo
format
anziché con una regex (altri formati ammessi sonodate
,time
,date-time
- campo tipo di secondo livello: enum, ovvero stringa ma che può assumere solo determinati valori
- campo classe di secondo livello: valore intero con limiti max e min
Da notare come il valore null debba essere inserito esplicitamente tra i tipi ammessi nonostante il campo che accetta null non sia specificato nell’elenco dei campi required
.
Conclusioni
Come dicevo all’inizio la scrittura di un json-schema secondo me dovrebbe e potrebbe essere evitata definendo e documentando opportunamente le api che generano i dati. In alcuni casi è comunque uno strumento che può tornarci utile: non solo può aiutare nei casi limite come quello di un’applicazione nello stadio iniziale con dati gestiti in modo minimale, ma anche quando i dati sono in possesso di soggetti terzi che non ne hanno particolare cura o che non ci forniscono appropriata documentazione l’idea di generarci uno schema può aiutarci a evitare spiacevoli inconvenienti, anche perché nessuno ci vieta di utilizzarlo nei nostri test di integrazione, per la serie “fidarsi è bene…”.