aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
blob: 5de340e5cae837b4507b3e0047abccf31ab7c23d (plain)
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
package com.fasterxml.jackson.databind.module;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

/**
 * Vanilla {@link Module} implementation that allows registration
 * of serializers and deserializers, bean serializer
 * and deserializer modifiers, registration of subtypes and mix-ins
 * as well as some other commonly
 * needed aspects (addition of custom {@link AbstractTypeResolver}s,
 * {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s).
 *<p>
 * NOTE: although it is not expected that sub-types should need to
 * override {@link #setupModule(SetupContext)} method, if they choose
 * to do so they MUST call <code>super.setupModule(context);</code>
 * to ensure that registration works as expected.
 *<p>
 * WARNING: when registering {@link JsonSerializer}s and {@link JsonDeserializer}s,
 * only type erased {@code Class} is compared: this means that usually you should
 * NOT use this implementation for registering structured types such as
 * {@link java.util.Collection}s or {@link java.util.Map}s: this because parametric
 * type information will not be considered and you may end up having "wrong" handler
 * for your type.
 * What you need to do, instead, is to implement {@link com.fasterxml.jackson.databind.deser.Deserializers} 
 * and/or {@link com.fasterxml.jackson.databind.ser.Serializers} callbacks to match full type
 * signatures (with {@link JavaType}).
 */
public class SimpleModule
    extends com.fasterxml.jackson.databind.Module
    implements java.io.Serializable
{
    private static final long serialVersionUID = 1L; // 2.5.0

    protected final String _name;
    protected final Version _version;

    protected SimpleSerializers _serializers = null;
    protected SimpleDeserializers _deserializers = null;

    protected SimpleSerializers _keySerializers = null;
    protected SimpleKeyDeserializers _keyDeserializers = null;

    /**
     * Lazily-constructed resolver used for storing mappings from
     * abstract classes to more specific implementing classes
     * (which may be abstract or concrete)
     */
    protected SimpleAbstractTypeResolver _abstractTypes = null;

    /**
     * Lazily-constructed resolver used for storing mappings from
     * abstract classes to more specific implementing classes
     * (which may be abstract or concrete)
     */
    protected SimpleValueInstantiators _valueInstantiators = null;

    /**
     * @since 2.2
     */
    protected BeanDeserializerModifier _deserializerModifier = null;

    /**
     * @since 2.2
     */
    protected BeanSerializerModifier _serializerModifier = null;

    /**
     * Lazily-constructed map that contains mix-in definitions, indexed
     * by target class, value being mix-in to apply.
     */
    protected HashMap<Class<?>, Class<?>> _mixins = null;
    
    /**
     * Set of subtypes to register, if any.
     */
    protected LinkedHashSet<NamedType> _subtypes = null;

    /**
     * @since 2.3
     */
    protected PropertyNamingStrategy _namingStrategy = null;
    
    /*
    /**********************************************************
    /* Life-cycle: creation
    /**********************************************************
     */

    /**
     * Constructors that should only be used for non-reusable
     * convenience modules used by app code: "real" modules should
     * use actual name and version number information.
     */
    public SimpleModule() {
        // can't chain when making reference to 'this'
        // note: generate different name for direct instantiation, sub-classing
        _name = (getClass() == SimpleModule.class) ?
                "SimpleModule-"+System.identityHashCode(this)
                : getClass().getName();
        _version = Version.unknownVersion();
    }
    
    /**
     * Convenience constructor that will default version to
     * {@link Version#unknownVersion()}.
     */
    public SimpleModule(String name) {
        this(name, Version.unknownVersion());
    }

    /**
     * Convenience constructor that will use specified Version,
     * including name from {@link Version#getArtifactId()}
     */
    public SimpleModule(Version version) {
        _name = version.getArtifactId();
        _version = version;
    }
    
    /**
     * Constructor to use for actual reusable modules.
     * ObjectMapper may use name as identifier to notice attempts
     * for multiple registrations of the same module (although it
     * does not have to).
     * 
     * @param name Unique name of the module
     * @param version Version of the module
     */
    public SimpleModule(String name, Version version) {
        _name = name;
        _version = version;
    }

    /**
     * @since 2.1
     */
    public SimpleModule(String name, Version version,
            Map<Class<?>,JsonDeserializer<?>> deserializers) {
        this(name, version, deserializers, null);
    }

    /**
     * @since 2.1
     */
    public SimpleModule(String name, Version version,
            List<JsonSerializer<?>> serializers) {
        this(name, version, null, serializers);
    }
    
    /**
     * @since 2.1
     */
    public SimpleModule(String name, Version version,
            Map<Class<?>,JsonDeserializer<?>> deserializers,
            List<JsonSerializer<?>> serializers)
    {
        _name = name;
        _version = version;
        if (deserializers != null) {
            _deserializers = new SimpleDeserializers(deserializers);
        }
        if (serializers != null) {
            _serializers = new SimpleSerializers(serializers);
        }
    }

    /**
     * Since instances are likely to be custom, implementation returns
     * <code>null</code> if (but only if!) this class is directly instantiated;
     * but class name (default impl) for sub-classes.
     */
    @Override
    public Object getTypeId() {
        if (getClass() == SimpleModule.class) {
            return null;
        }
        return super.getTypeId();
    }
    
    /*
    /**********************************************************
    /* Simple setters to allow overriding
    /**********************************************************
     */

    /**
     * Resets all currently configured serializers.
     */
    public void setSerializers(SimpleSerializers s) {
        _serializers = s;
    }

    /**
     * Resets all currently configured deserializers.
     */
    public void setDeserializers(SimpleDeserializers d) {
        _deserializers = d;
    }

    /**
     * Resets all currently configured key serializers.
     */
    public void setKeySerializers(SimpleSerializers ks) {
        _keySerializers = ks;
    }

    /**
     * Resets all currently configured key deserializers.
     */
    public void setKeyDeserializers(SimpleKeyDeserializers kd) {
        _keyDeserializers = kd;
    }

    /**
     * Resets currently configured abstract type mappings
     */
    public void setAbstractTypes(SimpleAbstractTypeResolver atr) {
        _abstractTypes = atr;        
    }

    /**
     * Resets all currently configured value instantiators
     */
    public void setValueInstantiators(SimpleValueInstantiators svi) {
        _valueInstantiators = svi;
    }

    /**
     * @since 2.2
     */
    public SimpleModule setDeserializerModifier(BeanDeserializerModifier mod) {
        _deserializerModifier = mod;
        return this;
    }

    /**
     * @since 2.2
     */
    public SimpleModule setSerializerModifier(BeanSerializerModifier mod) {
        _serializerModifier = mod;
        return this;
    }

    /**
     * @since 2.3
     */
    protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) {
        _namingStrategy = naming;
        return this;
    }
    
    /*
    /**********************************************************
    /* Configuration methods, adding serializers
    /**********************************************************
     */

    /**
     * Method for adding serializer to handle type that the serializer claims to handle
     * (see {@link JsonSerializer#handledType()}).
     *<p>
     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
     * be used when registering serializers for generic types like
     * {@link java.util.Collection} and {@link java.util.Map}.
     */
    public SimpleModule addSerializer(JsonSerializer<?> ser)
    {
        _checkNotNull(ser, "serializer");
        if (_serializers == null) {
            _serializers = new SimpleSerializers();
        }
        _serializers.addSerializer(ser);
        return this;
    }

    /**
     * Method for adding serializer to handle values of specific type.
     *<p>
     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
     * be used when registering serializers for generic types like
     * {@link java.util.Collection} and {@link java.util.Map}.
     */
    public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser)
    {
        _checkNotNull(type, "type to register serializer for");
        _checkNotNull(ser, "serializer");
        if (_serializers == null) {
            _serializers = new SimpleSerializers();
        }
        _serializers.addSerializer(type, ser);
        return this;
    }

    public <T> SimpleModule addKeySerializer(Class<? extends T> type, JsonSerializer<T> ser)
    {
        _checkNotNull(type, "type to register key serializer for");
        _checkNotNull(ser, "key serializer");
        if (_keySerializers == null) {
            _keySerializers = new SimpleSerializers();
        }
        _keySerializers.addSerializer(type, ser);
        return this;
    }

    /*
    /**********************************************************
    /* Configuration methods, adding deserializers
    /**********************************************************
     */
    
    /**
     * Method for adding deserializer to handle specified type.
     *<p>
     * WARNING! Type matching only uses type-erased {@code Class} and should NOT
     * be used when registering serializers for generic types like
     * {@link java.util.Collection} and {@link java.util.Map}.
     */
    public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)
    {
        _checkNotNull(type, "type to register deserializer for");
        _checkNotNull(deser, "deserializer");
        if (_deserializers == null) {
            _deserializers = new SimpleDeserializers();
        }
        _deserializers.addDeserializer(type, deser);
        return this;
    }

    public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser)
    {
        _checkNotNull(type, "type to register key deserializer for");
        _checkNotNull(deser, "key deserializer");
        if (_keyDeserializers == null) {
            _keyDeserializers = new SimpleKeyDeserializers();
        }
        _keyDeserializers.addDeserializer(type, deser);
        return this;
    }

    /*
    /**********************************************************
    /* Configuration methods, type mapping
    /**********************************************************
     */

    /**
     * Lazily-constructed resolver used for storing mappings from
     * abstract classes to more specific implementing classes
     * (which may be abstract or concrete)
     */
    public <T> SimpleModule addAbstractTypeMapping(Class<T> superType,
            Class<? extends T> subType)
    {
        _checkNotNull(superType, "abstract type to map");
        _checkNotNull(subType, "concrete type to map to");
        if (_abstractTypes == null) {
            _abstractTypes = new SimpleAbstractTypeResolver();
        }
        // note: addMapping() will verify arguments
        _abstractTypes = _abstractTypes.addMapping(superType, subType);
        return this;
    }

    /**
     * Method for adding set of subtypes to be registered with
     * {@link ObjectMapper}
     * this is an alternative to using annotations in super type to indicate subtypes.
     */
    public SimpleModule registerSubtypes(Class<?> ... subtypes)
    {
        if (_subtypes == null) {
            _subtypes = new LinkedHashSet<>();
        }
        for (Class<?> subtype : subtypes) {
            _checkNotNull(subtype, "subtype to register");
            _subtypes.add(new NamedType(subtype));
        }
        return this;
    }

    /**
     * Method for adding set of subtypes (along with type name to use) to be registered with
     * {@link ObjectMapper}
     * this is an alternative to using annotations in super type to indicate subtypes.
     */
    public SimpleModule registerSubtypes(NamedType ... subtypes)
    {
        if (_subtypes == null) {
            _subtypes = new LinkedHashSet<>();
        }
        for (NamedType subtype : subtypes) {
            _checkNotNull(subtype, "subtype to register");
            _subtypes.add(subtype);
        }
        return this;
    }

    /**
     * Method for adding set of subtypes (along with type name to use) to be registered with
     * {@link ObjectMapper}
     * this is an alternative to using annotations in super type to indicate subtypes.
     *
     * @since 2.9
     */
    public SimpleModule registerSubtypes(Collection<Class<?>> subtypes)
    {
        if (_subtypes == null) {
            _subtypes = new LinkedHashSet<>();
        }
        for (Class<?> subtype : subtypes) {
            _checkNotNull(subtype, "subtype to register");
            _subtypes.add(new NamedType(subtype));
        }
        return this;
    }

    /*
    /**********************************************************
    /* Configuration methods, add other handlers
    /**********************************************************
     */
    
    /**
     * Method for registering {@link ValueInstantiator} to use when deserializing
     * instances of type <code>beanType</code>.
     *<p>
     * Instantiator is
     * registered when module is registered for <code>ObjectMapper</code>.
     */
    public SimpleModule addValueInstantiator(Class<?> beanType, ValueInstantiator inst)
    {
        _checkNotNull(beanType, "class to register value instantiator for");
        _checkNotNull(inst, "value instantiator");
        if (_valueInstantiators == null) {
            _valueInstantiators = new SimpleValueInstantiators();
        }
        _valueInstantiators = _valueInstantiators.addValueInstantiator(beanType, inst);
        return this;
    }

    /**
     * Method for specifying that annotations define by <code>mixinClass</code>
     * should be "mixed in" with annotations that <code>targetType</code>
     * has (as if they were directly included on it!).
     *<p>
     * Mix-in annotations are
     * registered when module is registered for <code>ObjectMapper</code>.
     */
    public SimpleModule setMixInAnnotation(Class<?> targetType, Class<?> mixinClass)
    {
        _checkNotNull(targetType, "target type");
        _checkNotNull(mixinClass, "mixin class");
        if (_mixins == null) {
            _mixins = new HashMap<Class<?>, Class<?>>();
        }
        _mixins.put(targetType, mixinClass);
        return this;
    }

    /*
    /**********************************************************
    /* Module impl
    /**********************************************************
     */
    
    @Override
    public String getModuleName() {
        return _name;
    }

    /**
     * Standard implementation handles registration of all configured
     * customizations: it is important that sub-classes call this 
     * implementation (usually before additional custom logic)
     * if they choose to override it; otherwise customizations
     * will not be registered.
     */
    @Override
    public void setupModule(SetupContext context)
    {
        if (_serializers != null) {
            context.addSerializers(_serializers);
        }
        if (_deserializers != null) {
            context.addDeserializers(_deserializers);
        }
        if (_keySerializers != null) {
            context.addKeySerializers(_keySerializers);
        }
        if (_keyDeserializers != null) {
            context.addKeyDeserializers(_keyDeserializers);
        }
        if (_abstractTypes != null) {
            context.addAbstractTypeResolver(_abstractTypes);
        }
        if (_valueInstantiators != null) {
            context.addValueInstantiators(_valueInstantiators);
        }
        if (_deserializerModifier != null) {
            context.addBeanDeserializerModifier(_deserializerModifier);
        }
        if (_serializerModifier != null) {
            context.addBeanSerializerModifier(_serializerModifier);
        }
        if (_subtypes != null && _subtypes.size() > 0) {
            context.registerSubtypes(_subtypes.toArray(new NamedType[_subtypes.size()]));
        }
        if (_namingStrategy != null) {
            context.setNamingStrategy(_namingStrategy);
        }
        if (_mixins != null) {
            for (Map.Entry<Class<?>,Class<?>> entry : _mixins.entrySet()) {
                context.setMixInAnnotations(entry.getKey(), entry.getValue());
            }
        }
    }

    @Override
    public Version version() { return _version; }

    /*
    /**********************************************************
    /* Helper methods
    /**********************************************************
     */

    /**
     * @since 2.9
     */
    protected void _checkNotNull(Object thingy, String type)
    {
        if (thingy == null) {
            throw new IllegalArgumentException(String.format(
                    "Cannot pass `null` as %s", type));
        }
    }
}