Skip to content

Commit b215d4c

Browse files
committed
Add modulesToInstall(Modules...) to Jackson builder
This commit also adds a modules(Module...) method in addition to modules(List<Module> modules) in order to be consistent with other parts of the API. Issue: SPR-12634
1 parent 78f3a3c commit b215d4c

File tree

3 files changed

+124
-47
lines changed

3 files changed

+124
-47
lines changed

spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java

+53-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.text.DateFormat;
2020
import java.text.SimpleDateFormat;
21+
import java.util.Arrays;
2122
import java.util.HashMap;
2223
import java.util.LinkedHashMap;
2324
import java.util.LinkedList;
@@ -73,7 +74,7 @@
7374
*/
7475
public class Jackson2ObjectMapperBuilder {
7576

76-
private boolean createXmlMapper = false;
77+
private boolean createXmlMapper;
7778

7879
private DateFormat dateFormat;
7980

@@ -97,10 +98,12 @@ public class Jackson2ObjectMapperBuilder {
9798

9899
private List<Module> modules;
99100

100-
private Class<? extends Module>[] modulesToInstall;
101+
private Class<? extends Module>[] moduleClasses;
101102

102103
private boolean findModulesViaServiceLoader;
103104

105+
private boolean findWellKnownModules = true;
106+
104107
private ClassLoader moduleClassLoader = getClass().getClassLoader();
105108

106109
private HandlerInstantiator handlerInstantiator;
@@ -374,17 +377,51 @@ public Jackson2ObjectMapperBuilder featuresToDisable(Object... featuresToDisable
374377
return this;
375378
}
376379

380+
/**
381+
* Specify one or more modules to be registered with the {@link ObjectMapper}.
382+
* <p>Note: If this is set, no finding of modules is going to happen - not by
383+
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
384+
* As a consequence, specifying an empty list here will suppress any kind of
385+
* module detection.
386+
* <p>Specify either this or {@link #modulesToInstall}, not both.
387+
* @since 4.1.5
388+
* @see #modules(List)
389+
* @see com.fasterxml.jackson.databind.Module
390+
*/
391+
public Jackson2ObjectMapperBuilder modules(Module... modules) {
392+
return modules(Arrays.asList(modules));
393+
}
394+
377395
/**
378396
* Set a complete list of modules to be registered with the {@link ObjectMapper}.
379397
* <p>Note: If this is set, no finding of modules is going to happen - not by
380398
* Jackson, and not by Spring either (see {@link #findModulesViaServiceLoader}).
381399
* As a consequence, specifying an empty list here will suppress any kind of
382400
* module detection.
383401
* <p>Specify either this or {@link #modulesToInstall}, not both.
402+
* @see #modules(Module...)
384403
* @see com.fasterxml.jackson.databind.Module
385404
*/
386405
public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
387406
this.modules = new LinkedList<Module>(modules);
407+
this.findModulesViaServiceLoader = false;
408+
this.findWellKnownModules = false;
409+
return this;
410+
}
411+
412+
/**
413+
* Specify one or more modules to be registered with the {@link ObjectMapper}.
414+
* <p>Modules specified here will be registered after
415+
* Spring's autodetection of JSR-310 and Joda-Time, or Jackson's
416+
* finding of modules (see {@link #findModulesViaServiceLoader}),
417+
* allowing to eventually override their configuration.
418+
* <p>Specify either this or {@link #modules}, not both.
419+
* @since 4.1.5
420+
* @see com.fasterxml.jackson.databind.Module
421+
*/
422+
public Jackson2ObjectMapperBuilder modulesToInstall(Module... modules) {
423+
this.modules = Arrays.asList(modules);
424+
this.findWellKnownModules = true;
388425
return this;
389426
}
390427

@@ -396,10 +433,12 @@ public Jackson2ObjectMapperBuilder modules(List<Module> modules) {
396433
* finding of modules (see {@link #findModulesViaServiceLoader}),
397434
* allowing to eventually override their configuration.
398435
* <p>Specify either this or {@link #modules}, not both.
436+
* @see #modulesToInstall(Module...)
399437
* @see com.fasterxml.jackson.databind.Module
400438
*/
401439
public Jackson2ObjectMapperBuilder modulesToInstall(Class<? extends Module>... modules) {
402-
this.modulesToInstall = modules;
440+
this.moduleClasses = modules;
441+
this.findWellKnownModules = true;
403442
return this;
404443
}
405444

@@ -482,26 +521,23 @@ public <T extends ObjectMapper> T build() {
482521
public void configure(ObjectMapper objectMapper) {
483522
Assert.notNull(objectMapper, "ObjectMapper must not be null");
484523

524+
if (this.findModulesViaServiceLoader) {
525+
// Jackson 2.2+
526+
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
527+
}
528+
else if (this.findWellKnownModules) {
529+
registerWellKnownModulesIfAvailable(objectMapper);
530+
}
485531
if (this.modules != null) {
486532
// Complete list of modules given
487533
for (Module module : this.modules) {
488534
// Using Jackson 2.0+ registerModule method, not Jackson 2.2+ registerModules
489535
objectMapper.registerModule(module);
490536
}
491537
}
492-
else {
493-
// Combination of modules by class presence in the classpath and class names specified
494-
if (this.findModulesViaServiceLoader) {
495-
// Jackson 2.2+
496-
objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
497-
}
498-
else {
499-
registerWellKnownModulesIfAvailable(objectMapper);
500-
}
501-
if (this.modulesToInstall != null) {
502-
for (Class<? extends Module> module : this.modulesToInstall) {
503-
objectMapper.registerModule(BeanUtils.instantiate(module));
504-
}
538+
if (this.moduleClasses != null) {
539+
for (Class<? extends Module> module : this.moduleClasses) {
540+
objectMapper.registerModule(BeanUtils.instantiate(module));
505541
}
506542
}
507543

spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilderTests.java

+47-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
1616

1717
package org.springframework.http.converter.json;
1818

19+
import java.io.IOException;
1920
import java.io.UnsupportedEncodingException;
2021
import java.text.SimpleDateFormat;
2122
import java.util.ArrayList;
22-
import java.util.Arrays;
2323
import java.util.Collections;
2424
import java.util.Date;
2525
import java.util.HashMap;
@@ -41,6 +41,7 @@
4141
import com.fasterxml.jackson.databind.ObjectMapper;
4242
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
4343
import com.fasterxml.jackson.databind.SerializationFeature;
44+
import com.fasterxml.jackson.databind.SerializerProvider;
4445
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
4546
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
4647
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
@@ -55,11 +56,9 @@
5556
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
5657
import com.fasterxml.jackson.databind.type.SimpleType;
5758
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
58-
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;
59-
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
59+
import static org.hamcrest.Matchers.containsString;
6060
import org.joda.time.DateTime;
6161
import org.joda.time.DateTimeZone;
62-
import org.joda.time.format.DateTimeFormat;
6362
import org.junit.Test;
6463

6564
import org.springframework.beans.FatalBeanException;
@@ -212,15 +211,29 @@ public void wrongTimeZoneStringSetter() {
212211
}
213212

214213
@Test
215-
public void setModules() {
214+
public void modules() {
216215
NumberSerializer serializer1 = new NumberSerializer();
217216
SimpleModule module = new SimpleModule();
218217
module.addSerializer(Integer.class, serializer1);
219-
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(Arrays.asList(new Module[]{module})).build();
218+
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(module).build();
220219
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
221220
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null) == serializer1);
222221
}
223222

223+
@Test
224+
public void modulesToInstallByClass() {
225+
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
226+
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
227+
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
228+
}
229+
230+
@Test
231+
public void modulesToInstallByInstance() {
232+
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(new CustomIntegerModule()).build();
233+
Serializers serializers = getSerializerFactoryConfig(objectMapper).serializers().iterator().next();
234+
assertTrue(serializers.findSerializer(null, SimpleType.construct(Integer.class), null).getClass() == CustomIntegerSerializer.class);
235+
}
236+
224237
@Test
225238
public void defaultModules() throws JsonProcessingException, UnsupportedEncodingException {
226239
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
@@ -230,21 +243,29 @@ public void defaultModules() throws JsonProcessingException, UnsupportedEncoding
230243
}
231244

232245
@Test // SPR-12634
233-
public void customizeDefaultModules() throws JsonProcessingException, UnsupportedEncodingException {
246+
public void customizeDefaultModulesWithModule() throws JsonProcessingException, UnsupportedEncodingException {
234247
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
235-
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
236-
.modulesToInstall(CustomModule.class).build();
248+
.modulesToInstall(new CustomIntegerModule()).build();
237249
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
238-
assertEquals("\"2011-12-03\"", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
250+
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
251+
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
252+
}
253+
254+
@Test // SPR-12634
255+
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
256+
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modulesToInstall(CustomIntegerModule.class).build();
257+
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
258+
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
259+
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
239260
}
240261

241262
@Test // SPR-12634
242263
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
243264
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
244-
.serializerByType(DateTime.class, new DateTimeSerializer(new JacksonJodaDateFormat(DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC())))
245-
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build();
265+
.serializerByType(Integer.class, new CustomIntegerSerializer()).build();
246266
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
247-
assertEquals("\"2011-12-03\"", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
267+
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
268+
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
248269
}
249270

250271

@@ -387,7 +408,7 @@ public void createXmlMapper() {
387408
}
388409

389410

390-
public static class CustomModule extends Module {
411+
public static class CustomIntegerModule extends Module {
391412

392413
@Override
393414
public String getModuleName() {
@@ -402,9 +423,19 @@ public Version version() {
402423
@Override
403424
public void setupModule(SetupContext context) {
404425
SimpleSerializers serializers = new SimpleSerializers();
405-
serializers.addSerializer(DateTime.class, new DateTimeSerializer(new JacksonJodaDateFormat(DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC())));
426+
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
406427
context.addSerializers(serializers);
407428
}
408429
}
409430

431+
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
432+
433+
@Override
434+
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
435+
gen.writeStartObject();
436+
gen.writeNumberField("customid", value);
437+
gen.writeEndObject();
438+
}
439+
}
440+
410441
}

spring-web/src/test/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBeanTests.java

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.http.converter.json;
1818

19+
import java.io.IOException;
1920
import java.io.UnsupportedEncodingException;
2021
import java.text.SimpleDateFormat;
2122
import java.util.ArrayList;
@@ -40,6 +41,7 @@
4041
import com.fasterxml.jackson.databind.ObjectMapper;
4142
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
4243
import com.fasterxml.jackson.databind.SerializationFeature;
44+
import com.fasterxml.jackson.databind.SerializerProvider;
4345
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
4446
import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
4547
import com.fasterxml.jackson.databind.deser.BasicDeserializerFactory;
@@ -53,11 +55,9 @@
5355
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
5456
import com.fasterxml.jackson.databind.type.SimpleType;
5557
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
56-
import com.fasterxml.jackson.datatype.joda.cfg.JacksonJodaDateFormat;
57-
import com.fasterxml.jackson.datatype.joda.ser.DateTimeSerializer;
58+
import static org.hamcrest.Matchers.containsString;
5859
import org.joda.time.DateTime;
5960
import org.joda.time.DateTimeZone;
60-
import org.joda.time.format.DateTimeFormat;
6161
import org.junit.Before;
6262
import org.junit.Test;
6363

@@ -70,6 +70,7 @@
7070
*
7171
* @author <a href="mailto:[email protected]">Dmitry Katsubo</a>
7272
* @author Brian Clozel
73+
* @author Sebastien Deleuze
7374
*/
7475
public class Jackson2ObjectMapperFactoryBeanTests {
7576

@@ -242,28 +243,28 @@ public void defaultModules() throws JsonProcessingException, UnsupportedEncoding
242243
}
243244

244245
@Test // SPR-12634
245-
public void customizeDefaultModules() throws JsonProcessingException, UnsupportedEncodingException {
246-
this.factory.setFeaturesToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
247-
this.factory.setModulesToInstall(CustomModule.class);
246+
public void customizeDefaultModulesWithModuleClass() throws JsonProcessingException, UnsupportedEncodingException {
247+
this.factory.setModulesToInstall(CustomIntegerModule.class);
248248
this.factory.afterPropertiesSet();
249249
ObjectMapper objectMapper = this.factory.getObject();
250250

251251
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
252-
assertEquals("\"2011-12-03\"", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
252+
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
253+
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
253254
}
254255

255256
@Test // SPR-12634
256257
public void customizeDefaultModulesWithSerializer() throws JsonProcessingException, UnsupportedEncodingException {
257258
Map<Class<?>, JsonSerializer<?>> serializers = new HashMap<>();
258-
serializers.put(DateTime.class, new DateTimeSerializer(new JacksonJodaDateFormat(DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC())));
259+
serializers.put(Integer.class, new CustomIntegerSerializer());
259260

260261
this.factory.setSerializersByType(serializers);
261-
this.factory.setFeaturesToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
262262
this.factory.afterPropertiesSet();
263263
ObjectMapper objectMapper = this.factory.getObject();
264264

265265
DateTime dateTime = new DateTime(1322903730000L, DateTimeZone.UTC);
266-
assertEquals("\"2011-12-03\"", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
266+
assertEquals("1322903730000", new String(objectMapper.writeValueAsBytes(dateTime), "UTF-8"));
267+
assertThat(new String(objectMapper.writeValueAsBytes(new Integer(4)), "UTF-8"), containsString("customid"));
267268
}
268269

269270
@Test
@@ -396,8 +397,7 @@ public void createXmlMapper() {
396397
assertEquals(XmlMapper.class, this.factory.getObjectType());
397398
}
398399

399-
400-
public static class CustomModule extends Module {
400+
public static class CustomIntegerModule extends Module {
401401

402402
@Override
403403
public String getModuleName() {
@@ -412,9 +412,19 @@ public Version version() {
412412
@Override
413413
public void setupModule(SetupContext context) {
414414
SimpleSerializers serializers = new SimpleSerializers();
415-
serializers.addSerializer(DateTime.class, new DateTimeSerializer(new JacksonJodaDateFormat(DateTimeFormat.forPattern("YYYY-MM-dd").withZoneUTC())));
415+
serializers.addSerializer(Integer.class, new CustomIntegerSerializer());
416416
context.addSerializers(serializers);
417417
}
418418
}
419419

420+
public static class CustomIntegerSerializer extends JsonSerializer<Integer> {
421+
422+
@Override
423+
public void serialize(Integer value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
424+
gen.writeStartObject();
425+
gen.writeNumberField("customid", value);
426+
gen.writeEndObject();
427+
}
428+
}
429+
420430
}

0 commit comments

Comments
 (0)