3232import com .fasterxml .jackson .databind .JsonNode ;
3333import com .fasterxml .jackson .databind .ObjectMapper ;
3434import com .fasterxml .jackson .databind .ObjectMapper .DefaultTyping ;
35- import com .fasterxml .jackson .databind .ObjectReader ;
3635import com .fasterxml .jackson .databind .SerializerProvider ;
3736import com .fasterxml .jackson .databind .jsontype .PolymorphicTypeValidator ;
3837import com .fasterxml .jackson .databind .jsontype .TypeSerializer ;
5554 */
5655public class GenericJackson2JsonRedisSerializer implements RedisSerializer <Object > {
5756
58- private ObjectMapper mapper ;
57+ private final ObjectMapper mapper ;
5958
6059 private final JacksonObjectReader reader ;
6160
6261 private final JacksonObjectWriter writer ;
6362
64- private boolean internalReader = false ;
63+ private final Lazy < Boolean > defaultTypingEnabled ;
6564
6665 private final TypeResolver typeResolver ;
6766
68- private Lazy <Boolean > defaultTypingEnabled = Lazy
69- .of (() -> mapper .getSerializationConfig ().getDefaultTyper (null ) != null );
70-
71- private Lazy <String > typeHintPropertyName ;
72-
73- {
74- typeHintPropertyName = Lazy .of (() -> {
75- if (defaultTypingEnabled .get ()) {
76- return null ;
77- }
78-
79- return mapper .getDeserializationConfig ().getDefaultTyper (null )
80- .buildTypeDeserializer (mapper .getDeserializationConfig (), mapper .getTypeFactory ().constructType (Object .class ),
81- Collections .emptyList ())
82- .getPropertyName ();
83-
84- }).or ("@class" );
85-
86- typeResolver = new TypeResolver (Lazy .of (() -> mapper .getTypeFactory ()), typeHintPropertyName );
87- }
88-
8967 /**
9068 * Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
9169 */
@@ -104,7 +82,6 @@ public GenericJackson2JsonRedisSerializer() {
10482 */
10583 public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName ) {
10684 this (classPropertyTypeName , JacksonObjectReader .create (), JacksonObjectWriter .create ());
107- this .internalReader = true ;
10885 }
10986
11087 /**
@@ -122,7 +99,7 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
12299 public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName , JacksonObjectReader reader ,
123100 JacksonObjectWriter writer ) {
124101
125- this (new ObjectMapper (), reader , writer );
102+ this (new ObjectMapper (), reader , writer , classPropertyTypeName );
126103
127104 // simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need
128105 // the type hint embedded for deserialization using the default typing feature.
@@ -134,10 +111,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
134111 } else {
135112 mapper .activateDefaultTyping (mapper .getPolymorphicTypeValidator (), DefaultTyping .EVERYTHING , As .PROPERTY );
136113 }
137-
138- if (classPropertyTypeName != null ) {
139- typeHintPropertyName = Lazy .of (classPropertyTypeName );
140- }
141114 }
142115
143116 /**
@@ -149,7 +122,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
149122 */
150123 public GenericJackson2JsonRedisSerializer (ObjectMapper mapper ) {
151124 this (mapper , JacksonObjectReader .create (), JacksonObjectWriter .create ());
152- this .internalReader = true ;
153125 }
154126
155127 /**
@@ -164,6 +136,11 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) {
164136 */
165137 public GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
166138 JacksonObjectWriter writer ) {
139+ this (mapper , reader , writer , null );
140+ }
141+
142+ private GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
143+ JacksonObjectWriter writer , @ Nullable String typeHintPropertyName ) {
167144
168145 Assert .notNull (mapper , "ObjectMapper must not be null" );
169146 Assert .notNull (reader , "Reader must not be null" );
@@ -172,6 +149,29 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectRead
172149 this .mapper = mapper ;
173150 this .reader = reader ;
174151 this .writer = writer ;
152+
153+ this .defaultTypingEnabled = Lazy .of (() -> mapper .getSerializationConfig ().getDefaultTyper (null ) != null );
154+
155+ Supplier <String > typeHintPropertyNameSupplier ;
156+
157+ if (typeHintPropertyName == null ) {
158+
159+ typeHintPropertyNameSupplier = Lazy .of (() -> {
160+ if (defaultTypingEnabled .get ()) {
161+ return null ;
162+ }
163+
164+ return mapper .getDeserializationConfig ().getDefaultTyper (null )
165+ .buildTypeDeserializer (mapper .getDeserializationConfig (),
166+ mapper .getTypeFactory ().constructType (Object .class ), Collections .emptyList ())
167+ .getPropertyName ();
168+
169+ }).or ("@class" );
170+ } else {
171+ typeHintPropertyNameSupplier = () -> typeHintPropertyName ;
172+ }
173+
174+ this .typeResolver = new TypeResolver (Lazy .of (mapper ::getTypeFactory ), typeHintPropertyNameSupplier );
175175 }
176176
177177 /**
@@ -233,21 +233,22 @@ public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws Serializ
233233 }
234234 }
235235
236- protected JavaType resolveType (byte [] source , Class <?> type ) {
236+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
237237
238- if (internalReader || !type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
238+ if (!type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
239239 return typeResolver .constructType (type );
240240 }
241241
242242 return typeResolver .resolveType (source , type );
243243 }
244244
245- private static class TypeResolver {
245+ static class TypeResolver {
246246
247- private final ObjectReader objectReader = new ObjectMapper ().reader ();
247+ // need a separate instance to bypass class hint checks
248+ private final ObjectMapper mapper = new ObjectMapper ();
248249
249250 private final Supplier <TypeFactory > typeFactory ;
250- private Supplier <String > hintName ;
251+ private final Supplier <String > hintName ;
251252
252253 public TypeResolver (Supplier <TypeFactory > typeFactory , Supplier <String > hintName ) {
253254
@@ -259,15 +260,13 @@ protected JavaType constructType(Class<?> type) {
259260 return typeFactory .get ().constructType (type );
260261 }
261262
262- protected JavaType resolveType (byte [] source , Class <?> type ) {
263+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
263264
264- try {
265- TextNode typeName = (TextNode ) objectReader .readValue (source , JsonNode .class ).get (hintName .get ());
266- if (typeName != null ) {
267- return typeFactory .get ().constructFromCanonical (typeName .textValue ());
268- }
269- } catch (IOException e ) {
270- // TODO: logging?
265+ JsonNode root = mapper .readTree (source );
266+ JsonNode jsonNode = root .get (hintName .get ());
267+
268+ if (jsonNode instanceof TextNode && jsonNode .asText () != null ) {
269+ return typeFactory .get ().constructFromCanonical (jsonNode .asText ());
271270 }
272271
273272 return constructType (type );
0 commit comments