16 Jackson
Integration with the Jackson JSON marshalling library provides the ability to work with JSON.
The ratpack-jackson
JAR is released as part of Ratpack’s core distribution and is versioned with it. As of Ratpack 0.9.19 is built against (and depends on) Jackson Core 2.5.2.
The ratpack.jackson.Jackson
class provides most of the Jackson related functions.
16.1 Initialisation
The Jackson can be used with the Guice integration. The JacksonModule
is a Guice module that enables the integration.
If not using Guice, you can use the Jackson.Init.register()
method to add the necessary objects to the context registry.
16.2 Writing JSON responses
The Jackson integration adds a Renderer for rendering objects as JSON.
The Jackson.json()
method can be used to wrap any object (serializable by Jackson) for use with the Context.render()
method.
import ratpack.guice.Guice;
import ratpack.test.embed.EmbeddedApp;
import ratpack.jackson.guice.JacksonModule;
import ratpack.http.client.ReceivedResponse;
import static ratpack.jackson.Jackson.json;
import static org.junit.Assert.*;
public class Example {
public static class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String... args) throws Exception {
EmbeddedApp.of(s -> s
.registry(Guice.registry(b -> b.module(JacksonModule.class, c -> c.prettyPrint(false))))
.handlers(chain ->
chain.get(ctx -> ctx.render(json(new Person("John"))))
)
).test(httpClient -> {
ReceivedResponse response = httpClient.get();
assertEquals("{\"name\":\"John\"}", response.getBody().getText());
assertEquals("application/json", response.getBody().getContentType().getType());
});
}
}
See the Jackson
class documentation for more examples, including streaming and JSON events.
16.3 Reading JSON requests
The Jackson integration adds a Parser for converting JSON request bodies into objects.
The Jackson.jsonNode()
and Jackson.fromJson()
methods can be used to create objects to be used with the Context.parse()
method.
import ratpack.guice.Guice;
import ratpack.test.embed.EmbeddedApp;
import ratpack.jackson.guice.JacksonModule;
import ratpack.http.client.ReceivedResponse;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.reflect.TypeToken;
import java.util.List;
import static ratpack.util.Types.listOf;
import static ratpack.jackson.Jackson.jsonNode;
import static ratpack.jackson.Jackson.fromJson;
import static org.junit.Assert.*;
public class Example {
public static class Person {
private final String name;
public Person(@JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String... args) throws Exception {
EmbeddedApp.of(s -> s
.registry(Guice.registry(b -> b.module(JacksonModule.class, c -> c.prettyPrint(false))))
.handlers(chain -> chain
.post("asNode", ctx -> {
ctx.render(ctx.parse(jsonNode()).map(n -> n.get("name").asText()));
})
.post("asPerson", ctx -> {
ctx.render(ctx.parse(fromJson(Person.class)).map(p -> p.getName()));
})
.post("asPersonList", ctx -> {
ctx.render(ctx.parse(fromJson(listOf(Person.class))).map(p -> p.get(0).getName()));
})
)
).test(httpClient -> {
ReceivedResponse response = httpClient.requestSpec(s ->
s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
).post("asNode");
assertEquals("John", response.getBody().getText());
response = httpClient.requestSpec(s ->
s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
).post("asPerson");
assertEquals("John", response.getBody().getText());
response = httpClient.requestSpec(s ->
s.body(b -> b.type("application/json").text("[{\"name\":\"John\"}]"))
).post("asPersonList");
assertEquals("John", response.getBody().getText());
});
}
}
The integration adds a no opts parser, which makes it possible to use the Context.parse(Class)
and Context.parse(TypeToken)
methods.
import ratpack.guice.Guice;
import ratpack.test.embed.EmbeddedApp;
import ratpack.jackson.guice.JacksonModule;
import ratpack.http.client.ReceivedResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.reflect.TypeToken;
import java.util.List;
import static ratpack.util.Types.listOf;
import static org.junit.Assert.*;
public class Example {
public static class Person {
private final String name;
public Person(@JsonProperty("name") String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String... args) throws Exception {
EmbeddedApp.of(s -> s
.registry(Guice.registry(b -> b.module(JacksonModule.class, c -> c.prettyPrint(false))))
.handlers(chain -> chain
.post("asPerson", ctx -> {
ctx.parse(Person.class).then(person -> ctx.render(person.getName()));
})
.post("asPersonList", ctx -> {
ctx.parse(listOf(Person.class)).then(person -> ctx.render(person.get(0).getName()));
})
)
).test(httpClient -> {
ReceivedResponse response = httpClient.requestSpec(s ->
s.body(b -> b.type("application/json").text("{\"name\":\"John\"}"))
).post("asPerson");
assertEquals("John", response.getBody().getText());
response = httpClient.requestSpec(s ->
s.body(b -> b.type("application/json").text("[{\"name\":\"John\"}]"))
).post("asPersonList");
assertEquals("John", response.getBody().getText());
});
}
}
16.4 Using Jackson feature modules
Jackson feature modules allow Jackson to be extended to support extra data types and capabilities. For example the JDK8 module adds support for JDK8 types like Optional.
If using Guice, such modules can easily be registered via the module’s config.
import ratpack.guice.Guice;
import ratpack.test.embed.EmbeddedApp;
import ratpack.jackson.guice.JacksonModule;
import ratpack.http.client.ReceivedResponse;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import java.util.Optional;
import static ratpack.jackson.Jackson.json;
import static org.junit.Assert.*;
public class Example {
public static class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String... args) throws Exception {
EmbeddedApp.of(s -> s
.registry(Guice.registry(b -> b
.module(JacksonModule.class, c -> c
.modules(new Jdk8Module()) // register the Jackson module
.prettyPrint(false)
)
))
.handlers(chain ->
chain.get(ctx -> {
Optional<Person> personOptional = Optional.of(new Person("John"));
ctx.render(json(personOptional));
})
)
).test(httpClient -> {
ReceivedResponse response = httpClient.get();
assertEquals("{\"name\":\"John\"}", response.getBody().getText());
assertEquals("application/json", response.getBody().getContentType().getType());
});
}
}