Jersey and Gson

Now that I have the project legwork out of the way, I can add Gson support to our web application. Our first step is adding gson as a project dependency. This will direct maven to include gson and its dependencies in the project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git diff
diff --git a/pom.xml b/pom.xml
index 0fe5b04..eedf81b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,11 @@
<artifactId>jersey-media-moxy</artifactId>
</dependency>
-->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.3</version>
+ </dependency>
</dependencies>
<properties>
<jersey.version>2.12</jersey.version>

Next, I’ll pull in some code from REST with Java in practice by Lars Michael. These two classes implement the javax.ws.rs.ext.MesssageBodyWriter and javax.ws.rs.ext.MessageBodyReader interfaces necessary to marshall objects to and from json. The @Provider annotation allows JAX-RS autodiscovery to automatically find and install our classes. First, the GsonWriter class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Singleton
public class GsonWriter<T> implements MessageBodyWriter<T> {

@Override
public void writeTo(T t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream)

throws IOException, WebApplicationException {

Gson g = new Gson();
httpHeaders.get("Content-Type").add("charset=UTF-8");
entityStream.write(g.toJson(t).getBytes("UTF-8"));
}

@Override
public long getSize(T t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType)
{

return -1;
}

@Override
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType)
{

return true;
}
}

And the GsonReader class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Singleton
public class GsonReader<T> implements MessageBodyReader<T> {

@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] antns, MediaType mt)
{

return true;
}

@Override
public T readFrom(Class<T> type, Type genericType,
Annotation[] antns, MediaType mt,
MultivaluedMap<String, String> mm, InputStream in)

throws IOException, WebApplicationException {

Gson g = new Gson();
return g.fromJson(_convertStreamToString(in), type);
}

private String _convertStreamToString(InputStream inputStream)
throws IOException {

if (inputStream != null) {
Writer writer = new StringWriter();

char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(
new InputStreamReader(inputStream, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
inputStream.close();
}
return writer.toString();
} else {
return "";
}
}
}

A simple User class for us to marshall and unmarshall:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class User {
public static final String EMPTY_STRING = "";

protected String email;
protected String surName;
protected String givenName;

public User {
email = EMPTY_STRING;
surName = EMPTY_STRING;
givenName = EMPTY_STRING;
}

public String getEmail() { return email; }
public void setEmail(String em) { email = em; }

public String getSurName() { return surName; }
public void setSurName(String name) { surName = name; }

public String getGivenName() { return givenName; }
public void setGivenName(String name) { givenName = name; }
}

Finally, two new methods in the MyResource.java that will allow us to retrieve and post a User object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ git diff src/main/java/com/ideoplex/tutorial/MyResource.java
diff --git a/src/main/java/com/ideoplex/tutorial/MyResource.java b/src/main/java/com/ideoplex/tutorial/MyResource.java
index 13e8df0..8a932e5 100644
--- a/src/main/java/com/ideoplex/tutorial/MyResource.java
+++ b/src/main/java/com/ideoplex/tutorial/MyResource.java
@@ -1,7 +1,9 @@
package com.ideoplex.tutorial;

import javax.ws.rs.GET;
+import javax.ws.rs.POST;
import javax.ws.rs.Path;
+import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@@ -22,4 +24,25 @@ public class MyResource {
public String getIt() {
return "Got it!";
}
+
+ /**
+ * Methods to get and post a json User object
+ */

+ @GET
+ @Path("user/template")
+ @Produces(MediaType.APPLICATION_JSON)
+ public User getUserTemplate() {
+ User user = new User();
+ user.setEmail("noone@example.com");
+ user.setSurName("Doe");
+ user.setGivenName("John");
+ return user;
+ }
+
+ @POST
+ @Path("user/post")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public User postUser(User user) {
+ return user;
+ }
}

Now we can install our new web application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ mvn install
[INFO] Scanning for projects...
...

[INFO] Deploying war to http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/jersey-gson
Uploading: http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/manager/text/deploy?path=%2Fjersey-gson&update=true
Uploaded: http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/manager/text/deploy?path=%2Fjersey-gson&update=true (3794 KB at 667.4 KB/sec)

[INFO] tomcatManager status code:200, ReasonPhrase:OK
[INFO] OK - Deployed application at context path /jersey-gson
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:00 min
[INFO] Finished at: 2014-09-28T15:18:36-04:00
[INFO] Final Memory: 15M/245M
[INFO] ------------------------------------------------------------------------

And take it for a spin. First, an invocation of the old service.

1
2
$ curl http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/jersey-gson/webapi/myresource
Got it!%

Second, a GET request for the user template.

1
2
$ curl http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/jersey-gson/webapi/myresource/user/template
{"email":"noone@example.com","surName":"Doe","givenName":"John"}%

And finally, a POST upload of a user (I’ve used backslashes to split the command into smaller chunks).

1
2
3
4
$ curl http://xxxxxx-nnnnnn.use1-2.nitrousbox.com:8080/jersey-gson/webapi/myresource/user/post \
--header 'Content-type: application/json' \
--data '{"email":"jroe@example.com","surName":"Roe","givenName":"Jane"}'
{"email":"jroe@example.com","surName":"Roe","givenName":"Jane"}%

Browse the source for jersey-gson on GitHub.

29 Sep Custom marshalling with Gson - part 1