Mark Struberg ĂĽber die verbesserte JSON-Verarbeitung in Java EE 8
Thorben Janssen im Gespräch mit Mark Struberg über die Änderungen, die Java EE 8 mit den JSON-B-und JSON-P-Spezifikationen bringt.
- Thorben Janssen
Es gibt viele interessante Menschen in der Java Community, die mit ihrem Engagement in Java Specification Requests (JSRs) und Open-Source-Projekten die Entwicklung vorantreiben. Einige von ihnen möchte ich hier nach und nach vorstellen und mit ihnen über ihre Projekte sprechen. Dieses Mal habe ich mit Mark Struberg über Java EE 8 und Apaches JSON-P und JSON-B - Implementierung Johnzon gesprochen.
Thorben Janssen: Beim letzten Mal haben wir über deinen Einstieg in die Softwareentwicklung und deine Beteiligung an vielen Apache-Projekten gesprochen. Dieses Mal würde ich mit dir gerne über Java EE 8 reden. Viele Spezifikationen konnten in den letzten Monaten große Fortschritte verzeichnen und haben inzwischen die finale Version veröffentlicht oder stehen kurz davor. Welche Spezifikationen sind aus deiner Sicht besonders interessant?
Mark Struberg: Lange Zeit musste es fĂĽr AuĂźenstehende ja fast danach aussehen, als ob Java EE 8 gar nicht mehr erscheinen wĂĽrde. Der ursprĂĽngliche Plan war ja 2015, wurde aber dann recht bald auf 2016 verschoben. Und noch ist von einem Erscheinen wahrscheinlich im September 2017 die Rede.
Man kann Java EE 8 grob in zwei Bereiche aufteilen. Projekte, die von mit recht viel Elan an die Sache gingen und auch einen großen Umfang an neuen Funktionen vorzuweisen haben, und andere Spezifikationen, bei denen es kaum Fortschritt gab. Die Projekte mit vielen Neuerungen sind dabei durch die Bank durch die Community getrieben. Ich schätze, dass circa 85 Prozent aller funktionellen Neuerungen in Java EE aus der Community kommen.
Einige Spezifikationen und Features, die ich in Java EE 8 persönlich sehr gelungen finde, sind
- HTTP/2-Support in Servlet 4.0
- CDI 2.0 Async Events und vor allem programmatische Proxies fĂĽr Producer und Beans. Und zwar nicht auf Interface-Basis wie bei java.lang.reflect, sondern als Subclassing Proxies.
- JSON-B und ganz besonders seine Integration in JAX-RS
- JSON-P und hier vor allem der JsonPatch- und JsonMergePatch-Support
Janssen: Die JSON-B- und JSON-P-Spezifikationen liegen bereits als Final Releases vor. Was sind die aus deiner Sicht wichtigsten Änderungen und wie passen diese beiden Spezifikationen zusammen?
Struberg: Hier mĂĽssen wir zwischen JSON-P und JSON-B unterscheiden.
JSON Processing
FrĂĽhere Interviews mit
JSON-P steht fĂĽr JSON Processing, also das Lesen, Schreiben und Modifizieren von -SON Objekten aus Java.
Achtung. wenn man im Internet Infos über JSON-P sucht, da der Begriff auch für “JSON with Padding” verwendet wird, welches ein Mechanismus zum domänenübergreifenden Teilen von JSON-Informationen ist. Durch die inhärente Unsicherheit von JSON Padding ist das zwischenzeitlich aber sowieso durch CORS (Cross Origin Resource Sharing) ersetzt worden.
Zurück zu JSON-P. Zwar gibt es JSON-P schon seit Java EE 7, allerdings ist es bis jetzt noch nicht so bekannt, wie es eigentlich sein sollte. Mit JSON-P ist es wirklich sehr einfach geworden, aus Java heraus JSON zu schreiben und zu lesen. Dabei gibt es zwei grundsätzlich unterschiedliche Herangehensweisen, welche beide durch JSON-P unterstützt werden:
JsonObject
Ein Objekt-Baum aus lauter javax.json.JsonValue-Subklassen (JsonNumber, JsonString, …). Nehmen wir mal folgendes JSON-Objekt in einer Variable jsonString an:
{"name":"John Doe", "age":47, "address": {"zip": 12345, "street":"Blubstreet 7"}}
Wollen wir diese JSON-Struktur mit Java lesen, kann man dies folgendermaĂźen machen:
JsonReader jsonReader = javax.json.Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = jsonReader.readObject();
Das jsonObject erhält jetzt den gesamten Graphen des eingelesenen JSON-Strings. Das Ganze ist in etwa vergleichbar mit dem DOM-Baum bei XML, nur wesentlich speicherfreundlicher.
Man kann natürlich auch JSON-Werte als Objekte erstellen. Da wir den JsonProvider öfter brauchen, wollen wir ihn aus Performancegründen direkt holen:
JsonProvider jsonProvider = JsonProvider.provider();
Danach erstellen wir exakt das gleiche JSON,Objekt wie oben
JsonObject jsonObject =
jsonProvider.createObjectBuilder()
.add("name", "John Doe")
.add("age", 47)
.add("address", jsonProvider.createObjectBuilder()
.add("zip", 12345)
.add("street", "Blubstreet 7"))
.build();
Und natürlich können wir das Ganze auch noch ausgeben. Wenn man in einem Servlet ist und ein Servlet-Response-Objekt in der Hand hat, dann kann man direkt den Writer auf den OutputStream verwenden. In unserem Beispiel verwenden wir stattdessen einfach einen StringWriter:
StringWriter sw = new StringWriter();
JsonWriter jsonWriter = jsonProvider.createWriter(sw);
jsonWriter.writeObject(jsonObject);
System.out.println(sw.toString());
Wenn man sich nun vorstellt, dass wir aus einem Messsystem alle 100 ms ein JSON-Objekt schreiben wollen, dann kommen nach einer Zeit riesig große JSON-Objekte raus. Und die will man eventuell nicht als ein großes Java-JsonObject oder -JsonArray auf einen Schlag einlesen, da dies unnötig viel Speicher fressen würde.
Streaming
Und genau deshalb hat die JSON-P-Spezifikation zusätzlich auch einen "Streaming"-Modus. Wenn der JsonObject-Baum mit DOM verglichen wird, dann kann man das JSON-P-Streaming in etwa mit einem SAX-Parser vergleichen. Wir bekommen sukzessive Events, wenn ein JSON-Objekt gestartet, gelesen, beendet etc. wurde. Und können uns so unsere Information aus dem Stream rauspicken. Die dazugehörigen Klassen befinden sich im Paket javax.json.stream.*, zum Beispiel JsonParser.
Und mit dem JsonGenerator können wir auch einen JSON Stream schreiben.
Will man nun, wie schon angerissen, alle 100 ms einen Messwert zum Beispiel via WebSocket zu einem Client streamen, wird man hier mittels Json.createGenerator() ein JsonArray erstellen und so lange einzelne JsonObjects in das Array stellen, bis eine Abbruchanforderung kommt.
Das Meiste davon gab es bereits mit JSON-P 1.0, allerdings kamen in JSON-P 1.1 einige wichtige Funktionen dazu. Mit dem JsonReader kann man jetzt zum Beispiel auch "freistehende" JsonValues lesen und schreiben.
Vollständig neu sind hingegen JSON-Pointer (RFC-6901) und die Möglichkeiten zum Erstellen, Anwenden und Bearbeiten von JSON Patches (RFC-6902) und JSON Merge Patches (RFC-7386). Die beiden Letzteren erfüllen ähnliche Dienste, sind aber im Detail doch anders. Die Unterschiede werde ich später erklären, aber zuerst möchte ich kurz erläutern, für was man so was brauchen kann.
Nehmen wir an, wir haben auf einem Client ein großes JSON-Objekt eines Kunden mit all seinen bisherigen Einkäufen und wollen jetzt den Nachnamen ändern. Dann wäre es doch unvernünftig das gesamte JSON-Objekt zu übertragen. Das macht nicht nur viel Verkehr auf der Leitung, sondern könnte potenziell auch mit anderen Datenänderungen Konflikte erzeugen, zum Beispiel wenn in der Zwischenzeit ein neuer Einkauf über einen anderen Kanal hinzugekommen ist. Diese "konkurrierende" Änderung würde dann beinhart überschrieben werden. Und genau hier kommen die zwei JSON-Patch-Mechanismen ins Spiel, bei denen nur die Änderungen übertragen werden.
JSON Pointer
Zuerst aber noch kurz zu den JSON-Pointer. Diese sind sehr ähnlich zu XPath bei XML. Der Wurzelknoten wird durch ein slash ("/") repräsentiert, und danach kommen einfach die Attributnamen. Nehmen wir unser JSON aus unserem ersten Beispiel:
{"name":"John Doe", "age":47, "address": {"zip": 12345, "street":"Blubstreet 7"}}
Mit dem JSON-Pointer "/address/street" könnte ich jetzt die "Blubstreet 7" referenzieren.
Und mit JSON-Patch kommt gleich auch ein praktisches Beispiel.
JSON Patches
Ein JSON-Patch ist ein JSON-Array mit "Modifikations-Operationen", die auf ein Ziel-JSON angewendet werden, also quasi wie ein diff patch, zum Beispiel:
[{ "op": "replace", "path": "/address/zip", "value": 1010 },
{ "op": "add", "path": "/status", "value": “married” }]
Das path-Attribut jeder Zeile ist dabei ein JSON-Pointer, op die Operation, die auf das Zielobjekt angewendet werden soll.
JSON Patches ermöglichen komplexe Modifikationen eines JSON-Objektes, haben aber den Nachteil, doch eher viel Platz zu benötigen.
JSON Merge Patches
... sind im Vergleich platzsparender, aber auch ein wenig limitierter. Zum Beispiel erlauben sie keine vollständigen Array-Operationen. Sie sind ein JsonObject, das nur die zu ändernden Attribute enthält. Soll ein Attribut gelöscht werden, so wird es einfach auf null gesetzt:
{"age":48,"address":{"street":null}}
JSON-Binding
Kommen wir zu JSON-B, welches ja komplett neu hinzukommt. JSON-B könnte man mit JAXB vergleichen – also ein annotationsgetriebener Weg, JSON-Objekte in Java-Objekte zu konvertieren und umgekehrt.
Nachdem ich den Rahmen dieses Interviews sowieso schon gesprengt habe, werde ich eine detailliertere Betrachtung aussparen.
Ich will aber ganz kurz auf die Integration von JSON-B in JAX-RS eingehen. Bis jetzt musste man seine Datenobjekte ja mühsam selbst in JSON umwandeln. Oder man hat auf proprietäre Schnittstellen gesetzt und war damit aber nicht mehr portabel. Mit JSON-B hat dies nun ein Ende. Wenn ich ein Person.java-Objekt habe, dann kann ich nun einfach Folgendes schreiben, und JSON-B übernimmt die Umwandlung des Person-Objektes in einen JSON String:
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{personalnummer}")
public Person getByIsbn(@PathParam("personalnummer") String personalnummer{
return new Person(personalnummer);
}
Janssen: Wie bewertest du den aktuellen Stand der Spezifikationen? Sind im Rahmen von Java EE 9 noch weitere Änderungen zu erwarten?
Struberg: Wir haben im letzten halben Jahr noch mal sehr viele grundsätzliche Sachen in JSON-P 1.1 und JSON-B 1.0 gefixt. Die beiden Spezifikationen sind jetzt in der Praxis richtig gut benutzbar. Ich erwarte also nur minimale Änderungen.
Janssen: Welche ĂĽber die beiden Standards hinausgehenden Features sollte man als Entwickler kennen, wenn man mit Johnzon arbeitet?
Struberg: Im Gegensatz zu Jackson etc. war Apache Johnzon immer als Implementierung der Java-EE-Spezifikationen gedacht. Somit ist von der API eigentlich fast alles über die Spezifikation zugänglich. Trotzdem haben wir noch einige nette Features, die im Standard nicht enthalten sind: zum Beispiel die Behandlung von rekursiven Datenstrukturen und die optionale Auflösung derer mittels JSON-Pointer.
Will man mehr ĂĽber Johnzon erfahren, dann findet man diese Information entweder auf https://johnzon.apache.org oder man sendet uns ein Mail an mailto://dev@johnzon.apache.org
Janssen: Im vorherigen Interview hast du uns von deiner Beteiligung an TomEE erzählt. Da viele Apache-Projekte bereits an der Java-EE-8-Funktionalität arbeiten bzw. diese schon umgesetzt haben, liegt die Frage nahe, wann wir eine TomEE-Version für Java EE 8 erwarten können. Gibt es bereits einen Termin für eine finale Version?
Struberg: Leider gibt's noch keinen Termin, aber wir sind gerade dabei, die Einzelteile zusammenzutragen. Ich rechne damit, dass wir bis zur JavaOne zumindest mal einen ersten Wurf auf die Straße bekommen können.
Der aktuelle Plan ist, dass wir jetzt in der Übergangszeit zwischen Java EE 7 und Java EE 8 diverse 8.0.x-Versionen veröffentlichen, und sobald wir wirklich alles vollständig auf Java EE 8 haben, werden wir TomEE 8.1.0 herausbringen.
Janssen: Gibt es noch andere Projekte, an denen du arbeitest?
Struberg: Ja, neben meinen diversen Apache-Projekten (OpenWebBeans-2.0- als CDI-2.0-Implementierung ist zum Beispiel gerade raus) arbeite ich aktiv in der MicroProfile-Community mit.
Janssen: Wo kann man dich finden?
Struberg: Hauptsächlich auf den diversen Apache-Mailing-Listen. Daneben auch noch auf via IRC im Channel #openwebbeans auf irc.freenode.net und natürlich per @struberg auf Twitter.
Janssen: Vielen Dank fĂĽr das Interview und viel Erfolg mit deinen Projekten. ()