Skip to content

Commit 3189fd7

Browse files
committed
Jackson module: Add XML support and remove BlackBird
- Added JacksonModule.create(Module...) to customize defaults setup - Added JacksonModule(ObjectMapper, MediaType) for custom object mapper or content types - Removed BlackBirdModule from JacksonModule.create - Fixes jooby-project#2428 - Fixes jooby-project#2413
1 parent d96f0fa commit 3189fd7

File tree

8 files changed

+242
-25
lines changed

8 files changed

+242
-25
lines changed

docs/asciidoc/modules/jackson.adoc

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,52 @@ import io.jooby.json.JacksonModule
111111
}
112112
----
113113

114+
This allows to configure JacksonModule for doing `xml` processing:
115+
116+
.XmlMapper
117+
[source, java, role="primary"]
118+
----
119+
import io.jooby.json.JacksonModule;
120+
121+
{
122+
install(new JacksonModule(new XmlMapper()));
123+
}
124+
----
125+
126+
.Kotlin
127+
[source, kt, role="secondary"]
128+
----
129+
import io.jooby.json.JacksonModule
130+
131+
{
132+
install(JacksonModule(XmlMapper()))
133+
}
134+
----
135+
136+
If you want `json` and `xml` processing then install twice:
137+
138+
.XmlMapper
139+
[source, java, role="primary"]
140+
----
141+
import io.jooby.json.JacksonModule;
142+
143+
{
144+
install(new JacksonModule(new ObjectMapper()));
145+
install(new JacksonModule(new XmlMapper()));
146+
}
147+
----
148+
149+
.Kotlin
150+
[source, kt, role="secondary"]
151+
----
152+
import io.jooby.json.JacksonModule
153+
154+
{
155+
install(JacksonModule(ObjectMapper()))
156+
install(JacksonModule(XmlMapper()))
157+
}
158+
----
159+
114160
=== Provisioning Jackson Modules
115161

116162
Jackson module can be provided by a link:/#dependency-injection[dependency injection] framework.
@@ -135,4 +181,4 @@ import io.jooby.json.JacksonModule
135181
}
136182
----
137183

138-
At startup time Jooby ask to the dependency injection framework to provide a `MyModule` instance.
184+
At startup time Jooby ask to dependency injection framework to provide a `MyModule` instance.

modules/jooby-jackson/pom.xml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@
4646
<artifactId>jackson-module-parameter-names</artifactId>
4747
</dependency>
4848

49-
<dependency>
50-
<groupId>com.fasterxml.jackson.module</groupId>
51-
<artifactId>jackson-module-blackbird</artifactId>
52-
</dependency>
53-
5449
<!-- Test dependencies -->
5550
<dependency>
5651
<groupId>org.junit.jupiter</groupId>
@@ -70,5 +65,12 @@
7065
<artifactId>mockito-core</artifactId>
7166
<scope>test</scope>
7267
</dependency>
68+
69+
<dependency>
70+
<groupId>com.fasterxml.jackson.dataformat</groupId>
71+
<artifactId>jackson-dataformat-xml</artifactId>
72+
<scope>test</scope>
73+
</dependency>
74+
7375
</dependencies>
7476
</project>

modules/jooby-jackson/src/main/java/io/jooby/json/JacksonModule.java

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import com.fasterxml.jackson.databind.type.TypeFactory;
1414
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
1515
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
16-
import com.fasterxml.jackson.module.blackbird.BlackbirdModule;
1716
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
1817
import io.jooby.Body;
1918
import io.jooby.Context;
@@ -28,8 +27,11 @@
2827
import javax.annotation.Nonnull;
2928
import java.io.InputStream;
3029
import java.lang.reflect.Type;
30+
import java.util.HashMap;
3131
import java.util.HashSet;
32+
import java.util.Map;
3233
import java.util.Set;
34+
import java.util.stream.Stream;
3335

3436
/**
3537
* JSON module using Jackson: https://jooby.io/modules/jackson.
@@ -75,24 +77,43 @@
7577
* @since 2.0.0
7678
*/
7779
public class JacksonModule implements Extension, MessageDecoder, MessageEncoder {
80+
private final MediaType mediaType;
81+
7882
private final ObjectMapper mapper;
7983

8084
private final TypeFactory typeFactory;
8185

8286
private final Set<Class<? extends Module>> modules = new HashSet<>();
8387

88+
private static final Map<String, MediaType> defaultTypes = new HashMap<>();
89+
90+
static {
91+
defaultTypes.put("XmlMapper", MediaType.xml);
92+
}
93+
8494
/**
8595
* Creates a Jackson module.
8696
*
8797
* @param mapper Object mapper to use.
98+
* @param contentType Content type.
8899
*/
89-
public JacksonModule(@Nonnull ObjectMapper mapper) {
100+
public JacksonModule(@Nonnull ObjectMapper mapper, @Nonnull MediaType contentType) {
90101
this.mapper = mapper;
91102
this.typeFactory = mapper.getTypeFactory();
103+
this.mediaType = contentType;
104+
}
105+
106+
/**
107+
* Creates a Jackson module.
108+
*
109+
* @param mapper Object mapper to use.
110+
*/
111+
public JacksonModule(@Nonnull ObjectMapper mapper) {
112+
this(mapper, defaultTypes.getOrDefault(mapper.getClass().getSimpleName(), MediaType.json));
92113
}
93114

94115
/**
95-
* Creates a Jackson module using the default object mapper from {@link #create()}.
116+
* Creates a Jackson module using the default object mapper from {@link #create(Module...)}.
96117
*/
97118
public JacksonModule() {
98119
this(create());
@@ -111,11 +132,12 @@ public JacksonModule module(Class<? extends Module> module) {
111132
}
112133

113134
@Override public void install(@Nonnull Jooby application) {
114-
application.decoder(MediaType.json, this);
115-
application.encoder(MediaType.json, this);
135+
application.decoder(mediaType, this);
136+
application.encoder(mediaType, this);
116137

117138
ServiceRegistry services = application.getServices();
118-
services.put(ObjectMapper.class, mapper);
139+
Class mapperType = mapper.getClass();
140+
services.put(mapperType, mapper);
119141

120142
// Parsing exception as 400
121143
application.errorCode(JsonParseException.class, StatusCode.BAD_REQUEST);
@@ -129,7 +151,7 @@ public JacksonModule module(Class<? extends Module> module) {
129151
}
130152

131153
@Override public byte[] encode(@Nonnull Context ctx, @Nonnull Object value) throws Exception {
132-
ctx.setDefaultResponseType(MediaType.json);
154+
ctx.setDefaultResponseType(mediaType);
133155
return mapper.writer().writeValueAsBytes(value);
134156
}
135157

@@ -152,18 +174,19 @@ public JacksonModule module(Class<? extends Module> module) {
152174

153175
/**
154176
* Default object mapper. Install {@link Jdk8Module}, {@link JavaTimeModule},
155-
* {@link ParameterNamesModule} and {@link BlackbirdModule}.
177+
* {@link ParameterNamesModule}.
156178
*
179+
* @param modules Extra/additional modules to install.
157180
* @return Object mapper instance.
158181
*/
159-
public static @Nonnull ObjectMapper create() {
160-
ObjectMapper mapper = JsonMapper.builder()
182+
public static @Nonnull ObjectMapper create(Module... modules) {
183+
JsonMapper.Builder builder = JsonMapper.builder()
161184
.addModule(new ParameterNamesModule())
162185
.addModule(new Jdk8Module())
163-
.addModule(new JavaTimeModule())
164-
.addModule(new BlackbirdModule())
165-
.build();
186+
.addModule(new JavaTimeModule());
187+
188+
Stream.of(modules).forEach(builder::addModule);
166189

167-
return mapper;
190+
return builder.build();
168191
}
169192
}

modules/jooby-jackson/src/test/java/io/jooby/json/JacksonModuleTest.java renamed to modules/jooby-jackson/src/test/java/io/jooby/json/JacksonJsonModuleTest.java

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.jooby.json;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
35
import io.jooby.Body;
46
import io.jooby.Context;
57
import io.jooby.MediaType;
@@ -14,13 +16,13 @@
1416
import static org.mockito.Mockito.verify;
1517
import static org.mockito.Mockito.when;
1618

17-
public class JacksonModuleTest {
19+
public class JacksonJsonModuleTest {
1820

1921
@Test
20-
public void render() throws Exception {
22+
public void renderJson() throws Exception {
2123
Context ctx = mock(Context.class);
2224

23-
JacksonModule jackson = new JacksonModule();
25+
JacksonModule jackson = new JacksonModule(new ObjectMapper());
2426

2527
byte[] bytes = jackson.encode(ctx, mapOf("k", "v"));
2628
assertEquals("{\"k\":\"v\"}", new String(bytes, StandardCharsets.UTF_8));
@@ -29,7 +31,7 @@ public void render() throws Exception {
2931
}
3032

3133
@Test
32-
public void parse() throws Exception {
34+
public void parseJson() throws Exception {
3335
byte[] bytes = "{\"k\":\"v\"}".getBytes(StandardCharsets.UTF_8);
3436
Body body = mock(Body.class);
3537
when(body.isInMemory()).thenReturn(true);
@@ -38,7 +40,35 @@ public void parse() throws Exception {
3840
Context ctx = mock(Context.class);
3941
when(ctx.body()).thenReturn(body);
4042

41-
JacksonModule jackson = new JacksonModule();
43+
JacksonModule jackson = new JacksonModule(new ObjectMapper());
44+
45+
Map<String, String> result = (Map<String, String>) jackson.decode(ctx, Map.class);
46+
assertEquals(mapOf("k", "v"), result);
47+
}
48+
49+
@Test
50+
public void renderXml() throws Exception {
51+
Context ctx = mock(Context.class);
52+
53+
JacksonModule jackson = new JacksonModule(new XmlMapper());
54+
55+
byte[] bytes = jackson.encode(ctx, mapOf("k", "v"));
56+
assertEquals("<HashMap><k>v</k></HashMap>", new String(bytes, StandardCharsets.UTF_8));
57+
58+
verify(ctx).setDefaultResponseType(MediaType.xml);
59+
}
60+
61+
@Test
62+
public void parseXml() throws Exception {
63+
byte[] bytes = "<HashMap><k>v</k></HashMap>".getBytes(StandardCharsets.UTF_8);
64+
Body body = mock(Body.class);
65+
when(body.isInMemory()).thenReturn(true);
66+
when(body.bytes()).thenReturn(bytes);
67+
68+
Context ctx = mock(Context.class);
69+
when(ctx.body()).thenReturn(body);
70+
71+
JacksonModule jackson = new JacksonModule(new XmlMapper());
4272

4373
Map<String, String> result = (Map<String, String>) jackson.decode(ctx, Map.class);
4474
assertEquals(mapOf("k", "v"), result);

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,12 @@
600600
<version>${jackson.version}</version>
601601
</dependency>
602602

603+
<dependency>
604+
<groupId>com.fasterxml.jackson.dataformat</groupId>
605+
<artifactId>jackson-dataformat-xml</artifactId>
606+
<version>${jackson.version}</version>
607+
</dependency>
608+
603609
<dependency>
604610
<groupId>com.fasterxml.jackson.datatype</groupId>
605611
<artifactId>jackson-datatype-jdk8</artifactId>

tests/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@
172172
<version>${jetty.version}</version>
173173
</dependency>
174174

175+
<dependency>
176+
<groupId>com.fasterxml.jackson.dataformat</groupId>
177+
<artifactId>jackson-dataformat-xml</artifactId>
178+
<scope>test</scope>
179+
</dependency>
180+
175181
</dependencies>
176182

177183
<build>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.jooby.i2413;
2+
3+
public class B2413 {
4+
private String id;
5+
6+
private String name;
7+
8+
public B2413(String id, String name) {
9+
this.id = id;
10+
this.name = name;
11+
}
12+
13+
public String getId() {
14+
return id;
15+
}
16+
17+
public void setId(String id) {
18+
this.id = id;
19+
}
20+
21+
public String getName() {
22+
return name;
23+
}
24+
25+
public void setName(String name) {
26+
this.name = name;
27+
}
28+
}

0 commit comments

Comments
 (0)