6060 * @author Mark Paluch
6161 * @author Mao Shuai
6262 * @author John Blum
63+ * @author Anne Lee
6364 * @see org.springframework.data.redis.serializer.JacksonObjectReader
6465 * @see org.springframework.data.redis.serializer.JacksonObjectWriter
6566 * @see com.fasterxml.jackson.databind.ObjectMapper
@@ -92,13 +93,13 @@ public GenericJackson2JsonRedisSerializer() {
9293 * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS} will be
9394 * used.
9495 *
95- * @param classPropertyTypeName {@link String name} of the JSON property holding type information; can be
96+ * @param typeHintPropertyName {@link String name} of the JSON property holding type information; can be
9697 * {@literal null}.
9798 * @see ObjectMapper#activateDefaultTypingAsProperty(PolymorphicTypeValidator, DefaultTyping, String)
9899 * @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As)
99100 */
100- public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName ) {
101- this (classPropertyTypeName , JacksonObjectReader .create (), JacksonObjectWriter .create ());
101+ public GenericJackson2JsonRedisSerializer (@ Nullable String typeHintPropertyName ) {
102+ this (typeHintPropertyName , JacksonObjectReader .create (), JacksonObjectWriter .create ());
102103 }
103104
104105 /**
@@ -109,40 +110,22 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
109110 * In case {@link String name} is {@literal empty} or {@literal null}, then {@link JsonTypeInfo.Id#CLASS} will be
110111 * used.
111112 *
112- * @param classPropertyTypeName {@link String name} of the JSON property holding type information; can be
113+ * @param typeHintPropertyName {@link String name} of the JSON property holding type information; can be
113114 * {@literal null}.
114115 * @param reader {@link JacksonObjectReader} function to read objects using {@link ObjectMapper}.
115116 * @param writer {@link JacksonObjectWriter} function to write objects using {@link ObjectMapper}.
116117 * @see ObjectMapper#activateDefaultTypingAsProperty(PolymorphicTypeValidator, DefaultTyping, String)
117118 * @see ObjectMapper#activateDefaultTyping(PolymorphicTypeValidator, DefaultTyping, As)
118119 * @since 3.0
119120 */
120- public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName , JacksonObjectReader reader ,
121+ public GenericJackson2JsonRedisSerializer (@ Nullable String typeHintPropertyName , JacksonObjectReader reader ,
121122 JacksonObjectWriter writer ) {
122123
123- this (new ObjectMapper (), reader , writer , classPropertyTypeName );
124+ this (new ObjectMapper (), reader , writer , typeHintPropertyName );
124125
125- registerNullValueSerializer (this .mapper , classPropertyTypeName );
126+ registerNullValueSerializer (this .mapper , typeHintPropertyName );
126127
127- StdTypeResolverBuilder typer = TypeResolverBuilder .forEverything (this .mapper ).init (JsonTypeInfo .Id .CLASS , null )
128- .inclusion (JsonTypeInfo .As .PROPERTY );
129-
130- if (StringUtils .hasText (classPropertyTypeName )) {
131- typer = typer .typeProperty (classPropertyTypeName );
132- }
133-
134- this .mapper .setDefaultTyping (typer );
135- }
136-
137- /**
138- * Factory method returning a {@literal Builder} used to construct and configure a {@link GenericJackson2JsonRedisSerializer}.
139- *
140- * @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
141- * @since 3.3
142- */
143- public static GenericJackson2JsonRedisSerializerBuilder builder (ObjectMapper objectMapper , JacksonObjectReader reader ,
144- JacksonObjectWriter writer ) {
145- return new GenericJackson2JsonRedisSerializerBuilder (objectMapper , reader , writer );
128+ this .mapper .setDefaultTyping (createDefaultTypeResolverBuilder (getObjectMapper (), typeHintPropertyName ));
146129 }
147130
148131 /**
@@ -188,7 +171,7 @@ private GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectRea
188171 this .typeResolver = newTypeResolver (mapper , typeHintPropertyName , this .defaultTypingEnabled );
189172 }
190173
191- private TypeResolver newTypeResolver (ObjectMapper mapper , @ Nullable String typeHintPropertyName ,
174+ private static TypeResolver newTypeResolver (ObjectMapper mapper , @ Nullable String typeHintPropertyName ,
192175 Lazy <Boolean > defaultTypingEnabled ) {
193176
194177 Lazy <TypeFactory > lazyTypeFactory = Lazy .of (mapper ::getTypeFactory );
@@ -199,19 +182,17 @@ private TypeResolver newTypeResolver(ObjectMapper mapper, @Nullable String typeH
199182 return new TypeResolver (lazyTypeFactory , lazyTypeHintPropertyName );
200183 }
201184
202- private Lazy <String > newLazyTypeHintPropertyName (ObjectMapper mapper , Lazy <Boolean > defaultTypingEnabled ) {
185+ private static Lazy <String > newLazyTypeHintPropertyName (ObjectMapper mapper , Lazy <Boolean > defaultTypingEnabled ) {
203186
204187 Lazy <String > configuredTypeDeserializationPropertyName = getConfiguredTypeDeserializationPropertyName (mapper );
205188
206- Lazy <String > resolvedLazyTypeHintPropertyName = Lazy .of (() -> defaultTypingEnabled .get () ? null
207- : configuredTypeDeserializationPropertyName .get ());
208-
209- resolvedLazyTypeHintPropertyName = resolvedLazyTypeHintPropertyName .or ("@class" );
189+ Lazy <String > resolvedLazyTypeHintPropertyName = Lazy
190+ .of (() -> defaultTypingEnabled .get () ? null : configuredTypeDeserializationPropertyName .get ());
210191
211- return resolvedLazyTypeHintPropertyName ;
192+ return resolvedLazyTypeHintPropertyName . or ( "@class" ) ;
212193 }
213194
214- private Lazy <String > getConfiguredTypeDeserializationPropertyName (ObjectMapper mapper ) {
195+ private static Lazy <String > getConfiguredTypeDeserializationPropertyName (ObjectMapper mapper ) {
215196
216197 return Lazy .of (() -> {
217198
@@ -226,20 +207,43 @@ private Lazy<String> getConfiguredTypeDeserializationPropertyName(ObjectMapper m
226207 });
227208 }
228209
210+ private static StdTypeResolverBuilder createDefaultTypeResolverBuilder (ObjectMapper objectMapper ,
211+ @ Nullable String typeHintPropertyName ) {
212+
213+ StdTypeResolverBuilder typer = TypeResolverBuilder .forEverything (objectMapper ).init (JsonTypeInfo .Id .CLASS , null )
214+ .inclusion (As .PROPERTY );
215+
216+ if (StringUtils .hasText (typeHintPropertyName )) {
217+ typer = typer .typeProperty (typeHintPropertyName );
218+ }
219+ return typer ;
220+ }
221+
222+ /**
223+ * Factory method returning a {@literal Builder} used to construct and configure a
224+ * {@link GenericJackson2JsonRedisSerializer}.
225+ *
226+ * @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
227+ * @since 3.3.1
228+ */
229+ public static GenericJackson2JsonRedisSerializerBuilder builder () {
230+ return new GenericJackson2JsonRedisSerializerBuilder ();
231+ }
232+
229233 /**
230234 * Register {@link NullValueSerializer} in the given {@link ObjectMapper} with an optional
231- * {@code classPropertyTypeName }. This method should be called by code that customizes
235+ * {@code typeHintPropertyName }. This method should be called by code that customizes
232236 * {@link GenericJackson2JsonRedisSerializer} by providing an external {@link ObjectMapper}.
233237 *
234238 * @param objectMapper the object mapper to customize.
235- * @param classPropertyTypeName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
239+ * @param typeHintPropertyName name of the type property. Defaults to {@code @class} if {@literal null}/empty.
236240 * @since 2.2
237241 */
238- public static void registerNullValueSerializer (ObjectMapper objectMapper , @ Nullable String classPropertyTypeName ) {
242+ public static void registerNullValueSerializer (ObjectMapper objectMapper , @ Nullable String typeHintPropertyName ) {
239243
240244 // Simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here
241245 // since we need the type hint embedded for deserialization using the default typing feature.
242- objectMapper .registerModule (new SimpleModule ().addSerializer (new NullValueSerializer (classPropertyTypeName )));
246+ objectMapper .registerModule (new SimpleModule ().addSerializer (new NullValueSerializer (typeHintPropertyName )));
243247 }
244248
245249 /**
@@ -376,8 +380,7 @@ protected JavaType resolveType(byte[] source, Class<?> type) throws IOException
376380 */
377381 private static class NullValueSerializer extends StdSerializer <NullValue > {
378382
379- @ Serial
380- private static final long serialVersionUID = 1999052150548658808L ;
383+ @ Serial private static final long serialVersionUID = 1999052150548658808L ;
381384
382385 private final String classIdentifier ;
383386
@@ -408,65 +411,155 @@ public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, Seri
408411 }
409412
410413 /**
411- * {@literal Builder} for creating a {@link GenericJackson2JsonRedisSerializer}.
414+ * Builder for configuring and creating a {@link GenericJackson2JsonRedisSerializer}.
412415 *
413416 * @author Anne Lee
414- * @since 3.3
417+ * @author Mark Paluch
418+ * @since 3.3.1
415419 */
416420 public static class GenericJackson2JsonRedisSerializerBuilder {
417- @ Nullable
418- private String classPropertyTypeName ;
419- private JacksonObjectReader reader ;
420- private JacksonObjectWriter writer ;
421- private ObjectMapper mapper ;
422- @ Nullable
423- private StdSerializer <NullValue > nullValueSerializer ;
424-
425- private GenericJackson2JsonRedisSerializerBuilder (
426- ObjectMapper objectMapper ,
427- JacksonObjectReader reader ,
428- JacksonObjectWriter writer
429- ) {
430- this .mapper = objectMapper ;
421+
422+ private @ Nullable String typeHintPropertyName ;
423+
424+ private JacksonObjectReader reader = JacksonObjectReader .create ();
425+
426+ private JacksonObjectWriter writer = JacksonObjectWriter .create ();
427+
428+ private @ Nullable ObjectMapper objectMapper ;
429+
430+ private @ Nullable Boolean defaultTyping ;
431+
432+ private boolean registerNullValueSerializer = true ;
433+
434+ private @ Nullable StdSerializer <NullValue > nullValueSerializer ;
435+
436+ private GenericJackson2JsonRedisSerializerBuilder () {}
437+
438+ /**
439+ * Enable or disable default typing. Enabling default typing will override
440+ * {@link ObjectMapper#setDefaultTyping(com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder)} for a given
441+ * {@link ObjectMapper}. Default typing is enabled by default if no {@link ObjectMapper} is provided.
442+ *
443+ * @param defaultTyping whether to enable/disable default typing. Enabled by default if the {@link ObjectMapper} is
444+ * not provided.
445+ * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
446+ */
447+ public GenericJackson2JsonRedisSerializerBuilder defaultTyping (boolean defaultTyping ) {
448+ this .defaultTyping = defaultTyping ;
449+ return this ;
450+ }
451+
452+ /**
453+ * Configure a property name to that represents the type hint.
454+ *
455+ * @param typeHintPropertyName {@link String name} of the JSON property holding type information.
456+ * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
457+ */
458+ public GenericJackson2JsonRedisSerializerBuilder typeHintPropertyName (String typeHintPropertyName ) {
459+
460+ Assert .hasText (typeHintPropertyName , "Type hint property name must bot be null or empty" );
461+
462+ this .typeHintPropertyName = typeHintPropertyName ;
463+ return this ;
464+ }
465+
466+ /**
467+ * Configure a provided {@link ObjectMapper}. Note that the provided {@link ObjectMapper} can be reconfigured with a
468+ * {@link #nullValueSerializer} or default typing depending on the builder configuration.
469+ *
470+ * @param objectMapper must not be {@literal null}.
471+ * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
472+ */
473+ public GenericJackson2JsonRedisSerializerBuilder objectMapper (ObjectMapper objectMapper ) {
474+
475+ Assert .notNull (objectMapper , "ObjectMapper must not be null" );
476+
477+ this .objectMapper = objectMapper ;
478+ return this ;
479+ }
480+
481+ /**
482+ * Configure {@link JacksonObjectReader}.
483+ *
484+ * @param reader must not be {@literal null}.
485+ * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
486+ */
487+ public GenericJackson2JsonRedisSerializerBuilder reader (JacksonObjectReader reader ) {
488+
489+ Assert .notNull (reader , "JacksonObjectReader must not be null" );
490+
431491 this .reader = reader ;
432- this . writer = writer ;
492+ return this ;
433493 }
434494
435495 /**
436- * Configure a classPropertyName .
496+ * Configure {@link JacksonObjectWriter} .
437497 *
438- * @param classPropertyTypeName can be {@literal null}.
498+ * @param writer must not be {@literal null}.
439499 * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
440- * @since 3.3
441500 */
442- public GenericJackson2JsonRedisSerializerBuilder classPropertyTypeName (@ Nullable String classPropertyTypeName ) {
443- this .classPropertyTypeName = classPropertyTypeName ;
501+ public GenericJackson2JsonRedisSerializerBuilder writer (JacksonObjectWriter writer ) {
502+
503+ Assert .notNull (writer , "JacksonObjectWriter must not be null" );
504+
505+ this .writer = writer ;
444506 return this ;
445507 }
446508
447509 /**
448- * Register a nullValueSerializer .
510+ * Register a {@link StdSerializer serializer} for {@link NullValue} .
449511 *
450- * @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization. Can be {@literal null}.
512+ * @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization, must not be
513+ * {@literal null}.
451514 * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
452515 */
453- public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer (@ Nullable StdSerializer <NullValue > nullValueSerializer ) {
516+ public GenericJackson2JsonRedisSerializerBuilder nullValueSerializer (StdSerializer <NullValue > nullValueSerializer ) {
517+
518+ Assert .notNull (nullValueSerializer , "Null value serializer must not be null" );
519+
454520 this .nullValueSerializer = nullValueSerializer ;
455521 return this ;
456522 }
457523
458524 /**
459- * Create new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied.
525+ * Configure whether to register a {@link StdSerializer serializer} for {@link NullValue} serialization. The default
526+ * serializer considers {@link #typeHintPropertyName(String)}.
460527 *
461- * @return new instance of {@link GenericJackson2JsonRedisSerializer}.
528+ * @param registerNullValueSerializer {@code true} to register the default serializer; {@code false} otherwise.
529+ * @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
530+ */
531+ public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer (boolean registerNullValueSerializer ) {
532+ this .registerNullValueSerializer = registerNullValueSerializer ;
533+ return this ;
534+ }
535+
536+ /**
537+ * Creates a new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied. Creates
538+ * also a new {@link ObjectMapper} if none was provided.
539+ *
540+ * @return a new instance of {@link GenericJackson2JsonRedisSerializer}.
462541 */
463542 public GenericJackson2JsonRedisSerializer build () {
464- Assert .notNull (this .mapper , "ObjectMapper must not be null" );
465- Assert .notNull (this .reader , "Reader must not be null" );
466- Assert .notNull (this .writer , "Writer must not be null" );
467543
468- this .mapper .registerModule (new SimpleModule ().addSerializer (this .nullValueSerializer != null ? this .nullValueSerializer : new NullValueSerializer (this .classPropertyTypeName )));
469- return new GenericJackson2JsonRedisSerializer (this .mapper , this .reader , this .writer , this .classPropertyTypeName );
544+ ObjectMapper objectMapper = this .objectMapper ;
545+ boolean providedObjectMapper = objectMapper != null ;
546+
547+ if (objectMapper == null ) {
548+ objectMapper = new ObjectMapper ();
549+ }
550+
551+ if (registerNullValueSerializer ) {
552+ objectMapper .registerModule (new SimpleModule ("GenericJackson2JsonRedisSerializerBuilder" )
553+ .addSerializer (this .nullValueSerializer != null ? this .nullValueSerializer
554+ : new NullValueSerializer (this .typeHintPropertyName )));
555+ }
556+
557+ if ((!providedObjectMapper && (defaultTyping == null || defaultTyping ))
558+ || (defaultTyping != null && defaultTyping )) {
559+ objectMapper .setDefaultTyping (createDefaultTypeResolverBuilder (objectMapper , typeHintPropertyName ));
560+ }
561+
562+ return new GenericJackson2JsonRedisSerializer (objectMapper , this .reader , this .writer , this .typeHintPropertyName );
470563 }
471564 }
472565
0 commit comments