32
32
import com .fasterxml .jackson .databind .JsonNode ;
33
33
import com .fasterxml .jackson .databind .ObjectMapper ;
34
34
import com .fasterxml .jackson .databind .ObjectMapper .DefaultTyping ;
35
- import com .fasterxml .jackson .databind .ObjectReader ;
36
35
import com .fasterxml .jackson .databind .SerializerProvider ;
37
36
import com .fasterxml .jackson .databind .jsontype .PolymorphicTypeValidator ;
38
37
import com .fasterxml .jackson .databind .jsontype .TypeSerializer ;
55
54
*/
56
55
public class GenericJackson2JsonRedisSerializer implements RedisSerializer <Object > {
57
56
58
- private ObjectMapper mapper ;
57
+ private final ObjectMapper mapper ;
59
58
60
59
private final JacksonObjectReader reader ;
61
60
62
61
private final JacksonObjectWriter writer ;
63
62
64
- private boolean internalReader = false ;
63
+ private final Lazy < Boolean > defaultTypingEnabled ;
65
64
66
65
private final TypeResolver typeResolver ;
67
66
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
-
89
67
/**
90
68
* Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
91
69
*/
@@ -104,7 +82,6 @@ public GenericJackson2JsonRedisSerializer() {
104
82
*/
105
83
public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName ) {
106
84
this (classPropertyTypeName , JacksonObjectReader .create (), JacksonObjectWriter .create ());
107
- this .internalReader = true ;
108
85
}
109
86
110
87
/**
@@ -122,7 +99,7 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
122
99
public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName , JacksonObjectReader reader ,
123
100
JacksonObjectWriter writer ) {
124
101
125
- this (new ObjectMapper (), reader , writer );
102
+ this (new ObjectMapper (), reader , writer , classPropertyTypeName );
126
103
127
104
// simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need
128
105
// the type hint embedded for deserialization using the default typing feature.
@@ -134,10 +111,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
134
111
} else {
135
112
mapper .activateDefaultTyping (mapper .getPolymorphicTypeValidator (), DefaultTyping .EVERYTHING , As .PROPERTY );
136
113
}
137
-
138
- if (classPropertyTypeName != null ) {
139
- typeHintPropertyName = Lazy .of (classPropertyTypeName );
140
- }
141
114
}
142
115
143
116
/**
@@ -149,7 +122,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
149
122
*/
150
123
public GenericJackson2JsonRedisSerializer (ObjectMapper mapper ) {
151
124
this (mapper , JacksonObjectReader .create (), JacksonObjectWriter .create ());
152
- this .internalReader = true ;
153
125
}
154
126
155
127
/**
@@ -164,6 +136,11 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) {
164
136
*/
165
137
public GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
166
138
JacksonObjectWriter writer ) {
139
+ this (mapper , reader , writer , null );
140
+ }
141
+
142
+ private GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
143
+ JacksonObjectWriter writer , @ Nullable String typeHintPropertyName ) {
167
144
168
145
Assert .notNull (mapper , "ObjectMapper must not be null!" );
169
146
Assert .notNull (reader , "Reader must not be null!" );
@@ -172,6 +149,29 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectRead
172
149
this .mapper = mapper ;
173
150
this .reader = reader ;
174
151
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 );
175
175
}
176
176
177
177
/**
@@ -233,21 +233,22 @@ public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws Serializ
233
233
}
234
234
}
235
235
236
- protected JavaType resolveType (byte [] source , Class <?> type ) {
236
+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
237
237
238
- if (internalReader || !type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
238
+ if (!type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
239
239
return typeResolver .constructType (type );
240
240
}
241
241
242
242
return typeResolver .resolveType (source , type );
243
243
}
244
244
245
- private static class TypeResolver {
245
+ static class TypeResolver {
246
246
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 ();
248
249
249
250
private final Supplier <TypeFactory > typeFactory ;
250
- private Supplier <String > hintName ;
251
+ private final Supplier <String > hintName ;
251
252
252
253
public TypeResolver (Supplier <TypeFactory > typeFactory , Supplier <String > hintName ) {
253
254
@@ -259,15 +260,13 @@ protected JavaType constructType(Class<?> type) {
259
260
return typeFactory .get ().constructType (type );
260
261
}
261
262
262
- protected JavaType resolveType (byte [] source , Class <?> type ) {
263
+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
263
264
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 ());
271
270
}
272
271
273
272
return constructType (type );
0 commit comments