Riduzione delle dimensioni dei dati JSON

1. Introduzione

Le applicazioni Java utilizzano spesso JSON come formato comune per l'invio e la ricezione di dati. Inoltre, viene utilizzato come protocollo di serializzazione per la memorizzazione dei dati. Con dimensioni di dati JSON inferiori, le nostre applicazioni diventano più economiche e veloci.

In questo tutorial, esamineremo vari modi per ridurre la dimensione di JSON nelle nostre applicazioni Java.

2. Modello di dominio e dati di test

Creiamo un modello di dominio per un cliente con alcuni dati di contatto:

public class Customer { private long id; private String firstName; private String lastName; private String street; private String postalCode; private String city; private String state; private String phoneNumber; private String email;

Tieni presente che tutti i campi saranno obbligatori, ad eccezione di phoneNumber ed email .

Per testare correttamente le differenze di dimensione dei dati JSON, abbiamo bisogno di almeno alcune centinaia di istanze del cliente . Devono avere dati diversi per rendere i nostri test più realistici. Il sito web di generazione dei dati mockaroo ci aiuta qui. Possiamo creare 1.000 record di dati JSON lì gratuitamente, nel nostro formato e con dati di test autentici.

Configuriamo mockaroo per il nostro modello di dominio:

Ecco alcuni elementi da tenere a mente:

  • Qui è dove abbiamo specificato i nomi dei campi
  • Qui abbiamo selezionato i tipi di dati dei nostri campi
  • Il 50% dei numeri di telefono sono vuoti nei dati fittizi
  • Anche il 30% degli indirizzi e-mail sono vuoti

Tutti gli esempi di codice di seguito utilizzano gli stessi dati di 1.000 clienti di mockaroo . Usiamo il metodo di fabbrica Customer.fromMockFile () per leggere quel file e trasformarlo in oggetti Customer .

Useremo Jackson come nostra libreria di elaborazione JSON.

3. Dimensione dati JSON con opzioni predefinite Jackson

Scriviamo un oggetto Java in JSON con le opzioni Jackson predefinite:

Customer[] customers = Customer.fromMockFile(); ObjectMapper mapper = new ObjectMapper(); byte[] feedback = mapper.writeValueAsBytes(customers); 

Vediamo i dati fittizi per il primo cliente :

{ "id" : 1, "firstName" : "Horatius", "lastName" : "Strognell", "street" : "4848 New Castle Point", "postalCode" : "33432", "city" : "Boca Raton", "state" : "FL", "phoneNumber" : "561-824-9105", "email" : "[email protected]" }

Quando si utilizzano le opzioni Jackon predefinite, l'array di byte di dati JSON con tutti i 1.000 clienti ha una dimensione di 181,0 KB .

4. Comprimere con gzip

Come dati di testo, i dati JSON si comprimono bene. Ecco perché gzip è la nostra prima opzione per ridurre la dimensione dei dati JSON. Inoltre, può essere applicato automaticamente in HTTP, il protocollo comune per l'invio e la ricezione di JSON.

Prendiamo il JSON prodotto con le opzioni Jackson predefinite e comprimiamolo con gzip . Ciò si traduce in 45,9 KB, solo il 25,3% della dimensione originale . Quindi, se possiamo abilitare la compressione gzip tramite la configurazione, ridurremo la dimensione dei dati JSON del 75% senza alcuna modifica al nostro codice Java!

Se la nostra applicazione Spring Boot fornisce i dati JSON ad altri servizi o front-end, abiliteremo la compressione gzip nella configurazione Spring Boot. Vediamo una tipica configurazione di compressione nella sintassi YAML:

server: compression: enabled: true mime-types: text/html,text/plain,text/css,application/javascript,application/json min-response-size: 1024 

Innanzitutto, abbiamo abilitato la compressione in generale impostando abilitato come true. Quindi, abbiamo specificamente abilitato la compressione dei dati JSON aggiungendo application / json all'elenco dei tipi MIME . Infine, nota che abbiamo impostato la dimensione minima della risposta su una lunghezza di 1.024 byte. Questo perché se comprimiamo piccole quantità di dati, potremmo produrre dati più grandi dell'originale.

Spesso, proxy come NGINX o server Web come Apache HTTP Server forniscono i dati JSON ad altri servizi o front-end. La configurazione della compressione dei dati JSON in questi strumenti va oltre lo scopo di questo tutorial.

Un precedente tutorial su gzip ci dice che gzip ha vari livelli di compressione. I nostri esempi di codice utilizzano gzip con il livello di compressione Java predefinito. Spring Boot, proxy o server Web possono ottenere risultati di compressione diversi per gli stessi dati JSON.

Se usiamo JSON come protocollo di serializzazione per memorizzare i dati, dovremo comprimere e decomprimere i dati noi stessi.

5. Nomi di campo più brevi in ​​JSON

È buona norma utilizzare nomi di campo che non siano né troppo corti né troppo lunghi. Omettiamo questo per motivi di dimostrazione: useremo nomi di campo a carattere singolo in JSON, ma non cambieremo i nomi di campo Java. Ciò riduce la dimensione dei dati JSON ma riduce la leggibilità JSON. Poiché richiederebbe anche aggiornamenti a tutti i servizi e front-end, probabilmente utilizzeremo questi nomi di campo brevi solo durante la memorizzazione dei dati:

{ "i" : 1, "f" : "Horatius", "l" : "Strognell", "s" : "4848 New Castle Point", "p" : "33432", "c" : "Boca Raton", "a" : "FL", "o" : "561-824-9105", "e" : "[email protected]" }

È facile cambiare i nomi dei campi JSON con Jackson lasciando intatti i nomi dei campi Java. Useremo l' annotazione @JsonProperty :

@JsonProperty("p") private String postalCode; 

L'utilizzo di nomi di campo a carattere singolo porta a dati che sono il 72,5% della dimensione originale. Inoltre, l'utilizzo di gzip lo comprimerà al 23,8%. Non è molto più piccolo del 25,3% ottenuto dalla semplice compressione dei dati originali con gzip . Dobbiamo sempre cercare un rapporto costi-benefici adeguato. Perdere la leggibilità per un piccolo aumento di dimensioni non è consigliabile per la maggior parte degli scenari.

6. Serializzazione su un array

Vediamo come possiamo ridurre ulteriormente la dimensione dei dati JSON tralasciando del tutto i nomi dei campi. Possiamo ottenere ciò archiviando un array di clienti nel nostro JSON. Si noti che ridurremo anche la leggibilità. E dovremo anche aggiornare tutti i servizi e front-end che utilizzano i nostri dati JSON:

[ 1, "Horatius", "Strognell", "4848 New Castle Point", "33432", "Boca Raton", "FL", "561-824-9105", "[email protected]" ] 

L'archiviazione del cliente come array produce un output che è il 53,1% della dimensione originale e il 22,0% con compressione gzip . Questo è il nostro miglior risultato finora. Tuttavia, il 22% non è significativamente inferiore al 25,3% ottenuto dalla semplice compressione dei dati originali con gzip .

Per serializzare un cliente come array, dobbiamo assumere il pieno controllo della serializzazione JSON. Fare nuovamente riferimento al nostro tutorial su Jackson per ulteriori esempi.

7. Esclusi i valori nulli

Jackson and other JSON processing libraries may not handle JSON null values correctly when reading or writing JSON. For example, Jackson writes a JSON null value by default when it encounters a Java null value. That's why it's a good practice to remove empty fields in JSON data. This leaves the initialization of empty values to each JSON processing library and reduces the JSON data size.

In our mock data, we set 50% of the phone numbers, and 30% of the email addresses, as empty. Leaving out these null values reduces our JSON data size to 166.8kB or 92.1% of the original data size. Then, gzip compression will drop it to 24.9%.

Now, if we combine ignoring null values with the shorter field names from the previous section, then we'll get more significant savings: 68.3% of the original size and 23.4% with gzip.

We can configure the omission of null value fields in Jackson per class or globally for all classes.

8. New Domain Class

We achieved the smallest JSON data size so far by serializing it to an array. One way of reducing that even further is a new domain model with fewer fields. But why would we do that?

Let's imagine a front-end for our JSON data that shows all customers as a table with two columns: name and street address. Let's write JSON data specifically for this front-end:

{ "id" : 1, "name" : "Horatius Strognell", "address" : "4848 New Castle Point, Boca Raton FL 33432" }

Notice how we concatenated the name fields into name and the address fields into address. Also, we left out email and phoneNumber.

This should produce much smaller JSON data. It also saves the front-end from concatenating the Customer fields. But on the downside, this couples our back-end tightly to the front-end.

Let's create a new domain class CustomerSlim for this front-end:

public class CustomerSlim { private long id; private String name; private String address;

If we convert our test data to this new CustomerSlim domain class, we‘ll reduce it to 46.1% of the original size. That will be using the default Jackson settings. If we use gzip it goes down to 15.1%. This last result is already a significant gain over the previous best result of 22.0%.

Next, if we also use one-character field names, this gets us down to 40.7% of the original size, with gzip further reducing this to 14.7%. This result is only a small gain of over 15.1% we reached with the Jackson default settings.

No fields in CustomerSlim are optional, so leaving out empty values has no effect on the JSON data size.

La nostra ultima ottimizzazione è la serializzazione di un array. Serializzando CustomerSlim in un array, otteniamo il nostro miglior risultato: 34,2% della dimensione originale e 14,2% con gzip . Quindi, anche senza compressione, rimuoviamo quasi due terzi dei dati originali. E la compressione riduce i nostri dati JSON a solo un settimo della dimensione originale!

9. Conclusione

In questo articolo, abbiamo visto per la prima volta perché è necessario ridurre le dimensioni dei dati JSON. Successivamente, abbiamo imparato vari modi per ridurre questa dimensione dei dati JSON. Infine, abbiamo imparato come ridurre ulteriormente le dimensioni dei dati JSON con un modello di dominio personalizzato per un front-end.

Il codice completo è disponibile, come sempre, su GitHub.