Reduzieren der JSON-Datengröße

1. Einleitung

Java-Anwendungen verwenden JSON häufig als gängiges Format zum Senden und Empfangen von Daten. Darüber hinaus wird es als Serialisierungsprotokoll zum Speichern von Daten verwendet. Mit kleineren JSON-Datengrößen werden unsere Anwendungen billiger und schneller.

In diesem Tutorial werden verschiedene Möglichkeiten zur Reduzierung der Größe von JSON in unseren Java-Anwendungen vorgestellt.

2. Domänenmodell und Testdaten

Erstellen wir ein Domain-Modell für einen Kunden mit einigen Kontaktdaten:

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;

Beachten Sie, dass alle Felder mit Ausnahme von Telefonnummer und E-Mail obligatorisch sind .

Um die Unterschiede in der JSON-Datengröße ordnungsgemäß zu testen, benötigen wir mindestens einige hundert Kundeninstanzen . Sie müssen unterschiedliche Daten haben, um unsere Tests naturgetreuer zu gestalten. Die Datengenerierungs-Website mockaroo hilft uns hier. Dort können wir kostenlos 1.000 JSON-Datensätze in unserem eigenen Format und mit authentischen Testdaten erstellen.

Lassen Sie uns Mockaroo für unser Domain-Modell konfigurieren :

Hier einige Punkte, die Sie beachten sollten:

  • Hier haben wir die Feldnamen angegeben
  • Hier haben wir die Datentypen unserer Felder ausgewählt
  • 50% der Telefonnummern sind in den Scheindaten leer
  • 30% der E-Mail-Adressen sind ebenfalls leer

Alle folgenden Codebeispiele verwenden dieselben Daten von 1.000 Kunden von mockaroo . Wir verwenden die Factory-Methode Customer.fromMockFile () , um diese Datei zu lesen und in Kundenobjekte umzuwandeln.

Wir werden Jackson als unsere JSON-Verarbeitungsbibliothek verwenden.

3. JSON-Datengröße mit Jackson-Standardoptionen

Schreiben wir ein Java-Objekt mit den Standardoptionen von Jackson in JSON:

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

Sehen wir uns die Scheindaten für den ersten Kunden an :

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

Bei Verwendung der Standard-Jackon-Optionen ist das JSON-Datenbyte-Array mit allen 1.000 Kunden 181,0 KB groß .

4. Komprimieren mit gzip

Als Textdaten werden JSON-Daten gut komprimiert. Aus diesem Grund ist gzip unsere erste Option, um die JSON-Datengröße zu reduzieren. Darüber hinaus kann es automatisch in HTTP angewendet werden, dem allgemeinen Protokoll zum Senden und Empfangen von JSON.

Nehmen wir den mit den Standardoptionen von Jackson erstellten JSON und komprimieren ihn mit gzip . Dies ergibt 45,9 KB, nur 25,3% der Originalgröße . Wenn wir also die gzip- Komprimierung durch Konfiguration aktivieren können , reduzieren wir die JSON-Datengröße um 75%, ohne unseren Java-Code zu ändern!

Wenn unsere Spring Boot-Anwendung die JSON-Daten an andere Dienste oder Frontends liefert, aktivieren wir die gzip- Komprimierung in der Spring Boot-Konfiguration. Sehen wir uns eine typische Komprimierungskonfiguration in der YAML-Syntax an:

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

Zunächst haben wir die Komprimierung im Allgemeinen aktiviert, indem wir sie auf true gesetzt haben. Anschließend haben wir speziell die JSON-Datenkomprimierung aktiviert, indem wir application / json zur Liste der MIME-Typen hinzugefügt haben . Schließlich bemerken , dass wir setzen min-Antwort-Größe zu 1.024 Bytes lang. Dies liegt daran, dass wir beim Komprimieren kurzer Datenmengen möglicherweise größere Daten als das Original erzeugen.

Proxys wie NGINX oder Webserver wie der Apache HTTP Server liefern die JSON-Daten häufig an andere Dienste oder Frontends. Das Konfigurieren der JSON-Datenkomprimierung in diesen Tools geht über den Rahmen dieses Lernprogramms hinaus.

Ein vorheriges Tutorial zu gzip hat uns gezeigt, dass gzip verschiedene Komprimierungsstufen hat. Unsere Codebeispiele verwenden gzip mit der Standard-Java-Komprimierungsstufe. Spring Boot, Proxys oder Webserver erhalten möglicherweise unterschiedliche Komprimierungsergebnisse für dieselben JSON-Daten.

Wenn wir JSON als Serialisierungsprotokoll zum Speichern von Daten verwenden, müssen wir die Daten selbst komprimieren und dekomprimieren.

5. Kürzere Feldnamen in JSON

It's a best practice to use field names that are neither too short nor too long. Let's omit this for the sake of demonstration: We'll use single-character field names in JSON, but we'll not change the Java field names. This reduces the JSON data size but lowers JSON readability. Since it would also require updates to all services and front-ends, we'll probably use these short field names only when storing data:

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

It's easy to change the JSON field names with Jackson while leaving the Java field names intact. We'll use the @JsonProperty annotation:

@JsonProperty("p") private String postalCode; 

Using single-character field names leads to data that is 72.5% of the original size. Moreover, using gzip will compress that to 23.8%. That's not much smaller than the 25.3% we got from simply compressing the original data with gzip. We always need to look for a suitable cost-benefit relation. Losing readability for a small gain in size won't be recommendable for most scenarios.

6. Serializing to an Array

Let's see how we can further reduce the JSON data size by leaving out the field names altogether. We can achieve this by storing a customers array in our JSON. Notice that we'll be also reducing readability. And we'll also need to update all the services and front-ends that use our JSON data:

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

Storing the Customer as an array leads to output that's 53.1% of the original size, and 22.0% with gzip compression. This is our best result so far. Still, 22% is not significantly smaller than the 25.3% we got from merely compressing the original data with gzip.

In order to serialize a customer as an array, we need to take full control of JSON serialization. Refer again to our Jackson tutorial for more examples.

7. Excluding null Values

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.

Unsere letzte Optimierung ist die Serialisierung eines Arrays. Durch die Serialisierung von CustomerSlim in ein Array erzielen wir unser bestes Ergebnis: 34,2% der Originalgröße und 14,2% mit gzip . Selbst ohne Komprimierung entfernen wir fast zwei Drittel der Originaldaten. Durch die Komprimierung werden unsere JSON-Daten auf nur ein Siebtel der Originalgröße verkleinert!

9. Fazit

In diesem Artikel haben wir zuerst gesehen, warum wir die JSON-Datengröße reduzieren müssen. Als Nächstes haben wir verschiedene Möglichkeiten kennengelernt, um diese JSON-Datengröße zu reduzieren. Schließlich haben wir gelernt, wie Sie die JSON-Datengröße mit einem Domänenmodell, das für ein Front-End benutzerdefiniert ist, weiter reduzieren können.

Der vollständige Code ist wie immer auf GitHub verfügbar.