RESTful JSON mit Jersey und Spring
28. Juli 2010 von Dirk Dittmar [permalink]Vor einiger Zeit hatte ich einen Artikel geschrieben wie man mit Jersey und Spring einen REST Service erstellt. In dem Artikel hatte das Programm XML als Austauschformat benutzt. Da XML manchmal ein bisschen Fett ist und sich von einigen Sprachen (z.B. PHP) schwerer verarbeiten lässt, wollen wir uns dieses mal ansehen wie man JSON statt XML erzeugen kann.
Die Entwickler von Jersey haben da natürlich schon was vorbereitet. Erstmal muss man natürlich die richtige Library einbinden:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey-version}</version>
</dependency>
Da JSON nicht über so etwas wie XSD’s verfügt kann man sich den Aufwand mit der XSD sparen und die Beans direkt definieren:
@XmlRootElement
public class Person {
private int id;
private String firstName;
private String lastName;
private Date date;
// getter and setter omitted
}
Jersey verwendet zum serialisieren in JSON JAXB. Deshalb muss man jedes Bean das über die Leitung gehen soll mit @XmlRootElement annotieren. Die Beans könnte sonst nicht serialisiert werden.
Jetzt muss man nur noch in der Resource-Klasse angeben das diese Methode JSON erzeugt:
@GET
@Path("one")
@Produces(MediaType.APPLICATION_JSON)
public List getPersonsOne() {
return Collections.singletonList(new Person("Dirk", "Dittmar"));
}
Jetzt kann man den Jetty-Server starten (mvn jetty:run) und man erhält mit:
curl http://localhost:8080/jersey-spring-json/persons/one/
dieses JSON (formatiert für bessere Lesbarkeit):
{
"person": {
"date": "2010-07-27T22:06:17.581+02:00",
"firstName": "Dirk",
"id": "0",
"lastName": "Dittmar"
}
}
Das war einfach, oder? Leider hat diese Lösung einige Probleme
- Wie man an dem Beispiel sieht erzeugt die Library kein JSON Array wenn nur ein Element in der Liste ist. Das ist zumindest etwas ungewöhnlich und erschwert die automatisch Verarbeitung auf dem Client.
- Wenn man eine leere List zurück gibt erzeugt die Library einfach ein
null. Was sicher kein valides JSON ist.
Schade eigentlich, aber das währe ja auch zu einfach gewesen
Zum Glück gibt es im JAX-RS Standard einige Erweiterungs-Punkte. So kann man zu erzeugen des JSON’s einen eigenen Provider schreiben. Also sucht man sich eine anständige Library zum JSON verarbeiten und bindet sie ein (erstmal jersey-json rausnehmen):
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
Dann muss man noch den Provider schreiben (muss MessageBodyWriter implementieren):
@Provider
@Singleton
public class JSONMessageBodyWriter implements MessageBodyWriter {
private JsonConfig config = new JsonConfig();
public JSONMessageBodyWriter() {
config.registerJsonBeanProcessor(Date.class, new JsonBeanProcessor() {
@Override
public JSONObject processBean(Object bean, JsonConfig jsonConfig) {
JSONObject obj = new JSONObject();
return obj.element("time", ((Date) bean).getTime());
}
});
}
@Override
public long getSize(Object t, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public boolean isWriteable(Class type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return mediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE);
}
@Override
public void writeTo(Object t, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap httpHeaders,
OutputStream entityStream) throws IOException,
WebApplicationException {
entityStream.write(JSONSerializer.toJSON(t, config).toString()
.getBytes("UTF-8"));
}
}
Der Provider hat seine eigene Behandlung für java.util.Date Objekte, da die Library einfach alle Getter der Klasse abruft und in das JSON packt. Leider ist das bei java.util.Date etwas unschön aber man kann ja hier leicht Abhilfe schaffen.
Wenn man die Änderungen durchgeführt hat bekommt man auch valides JSON geliefert. So erhält man bei einer leeren Liste das leere Array ([]) und bei einer List mit nur einem Element bekommt man einfach ein Array mit nur einem Element (formatiert für bessere Lesbarkeit):
[
{
"date": {
"time": 1280261298440
},
"firstName": "Dirk",
"id": 0,
"lastName": "Dittmar"
}
]
Und als Bonus bekommt man auch noch die korrekte JSON Darstellung der verschiedenen Typen! Man beachte das “id” und “time” korrekt als Zahlen dargestellt werden und nicht wie vorher als Strings.
Jetzt muss man sicherlich auch einen eigenen MessageBodyReader implementieren aber dazu vielleicht später mehr.
Hier mein Beispiel-Projekt (Maven2): jersey-spring-json.zip
Tags: JAX-RS, jersey, jetty, JSON, Spring
Kategorie: Allgemein, Java, Softwareentwicklung
