17
17
18
18
import java .io .IOException ;
19
19
import java .util .Collections ;
20
+ import java .util .Map ;
20
21
import java .util .function .Supplier ;
21
22
22
23
import org .springframework .cache .support .NullValue ;
29
30
import com .fasterxml .jackson .annotation .JsonTypeInfo .As ;
30
31
import com .fasterxml .jackson .core .JsonGenerator ;
31
32
import com .fasterxml .jackson .databind .JavaType ;
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 ;
39
38
import com .fasterxml .jackson .databind .module .SimpleModule ;
40
- import com .fasterxml .jackson .databind .node .TextNode ;
41
39
import com .fasterxml .jackson .databind .ser .SerializerFactory ;
42
40
import com .fasterxml .jackson .databind .ser .std .StdSerializer ;
43
41
import com .fasterxml .jackson .databind .type .TypeFactory ;
55
53
*/
56
54
public class GenericJackson2JsonRedisSerializer implements RedisSerializer <Object > {
57
55
58
- private ObjectMapper mapper ;
56
+ private final ObjectMapper mapper ;
59
57
60
58
private final JacksonObjectReader reader ;
61
59
62
60
private final JacksonObjectWriter writer ;
63
61
64
- private boolean internalReader = false ;
62
+ private final Lazy < Boolean > defaultTypingEnabled ;
65
63
66
64
private final TypeResolver typeResolver ;
67
65
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
66
/**
90
67
* Creates {@link GenericJackson2JsonRedisSerializer} and configures {@link ObjectMapper} for default typing.
91
68
*/
@@ -104,7 +81,6 @@ public GenericJackson2JsonRedisSerializer() {
104
81
*/
105
82
public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName ) {
106
83
this (classPropertyTypeName , JacksonObjectReader .create (), JacksonObjectWriter .create ());
107
- this .internalReader = true ;
108
84
}
109
85
110
86
/**
@@ -122,7 +98,7 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
122
98
public GenericJackson2JsonRedisSerializer (@ Nullable String classPropertyTypeName , JacksonObjectReader reader ,
123
99
JacksonObjectWriter writer ) {
124
100
125
- this (new ObjectMapper (), reader , writer );
101
+ this (new ObjectMapper (), reader , writer , classPropertyTypeName );
126
102
127
103
// simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need
128
104
// the type hint embedded for deserialization using the default typing feature.
@@ -134,10 +110,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
134
110
} else {
135
111
mapper .activateDefaultTyping (mapper .getPolymorphicTypeValidator (), DefaultTyping .EVERYTHING , As .PROPERTY );
136
112
}
137
-
138
- if (classPropertyTypeName != null ) {
139
- typeHintPropertyName = Lazy .of (classPropertyTypeName );
140
- }
141
113
}
142
114
143
115
/**
@@ -149,7 +121,6 @@ public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName
149
121
*/
150
122
public GenericJackson2JsonRedisSerializer (ObjectMapper mapper ) {
151
123
this (mapper , JacksonObjectReader .create (), JacksonObjectWriter .create ());
152
- this .internalReader = true ;
153
124
}
154
125
155
126
/**
@@ -164,6 +135,11 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper) {
164
135
*/
165
136
public GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
166
137
JacksonObjectWriter writer ) {
138
+ this (mapper , reader , writer , null );
139
+ }
140
+
141
+ private GenericJackson2JsonRedisSerializer (ObjectMapper mapper , JacksonObjectReader reader ,
142
+ JacksonObjectWriter writer , @ Nullable String typeHintPropertyName ) {
167
143
168
144
Assert .notNull (mapper , "ObjectMapper must not be null!" );
169
145
Assert .notNull (reader , "Reader must not be null!" );
@@ -172,6 +148,29 @@ public GenericJackson2JsonRedisSerializer(ObjectMapper mapper, JacksonObjectRead
172
148
this .mapper = mapper ;
173
149
this .reader = reader ;
174
150
this .writer = writer ;
151
+
152
+ this .defaultTypingEnabled = Lazy .of (() -> mapper .getSerializationConfig ().getDefaultTyper (null ) != null );
153
+
154
+ Supplier <String > typeHintPropertyNameSupplier ;
155
+
156
+ if (typeHintPropertyName == null ) {
157
+
158
+ typeHintPropertyNameSupplier = Lazy .of (() -> {
159
+ if (defaultTypingEnabled .get ()) {
160
+ return null ;
161
+ }
162
+
163
+ return mapper .getDeserializationConfig ().getDefaultTyper (null )
164
+ .buildTypeDeserializer (mapper .getDeserializationConfig (),
165
+ mapper .getTypeFactory ().constructType (Object .class ), Collections .emptyList ())
166
+ .getPropertyName ();
167
+
168
+ }).or ("@class" );
169
+ } else {
170
+ typeHintPropertyNameSupplier = () -> typeHintPropertyName ;
171
+ }
172
+
173
+ this .typeResolver = new TypeResolver (Lazy .of (mapper ::getTypeFactory ), typeHintPropertyNameSupplier );
175
174
}
176
175
177
176
/**
@@ -233,21 +232,22 @@ public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws Serializ
233
232
}
234
233
}
235
234
236
- protected JavaType resolveType (byte [] source , Class <?> type ) {
235
+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
237
236
238
- if (internalReader || !type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
237
+ if (!type .equals (Object .class ) || !defaultTypingEnabled .get ()) {
239
238
return typeResolver .constructType (type );
240
239
}
241
240
242
241
return typeResolver .resolveType (source , type );
243
242
}
244
243
245
- private static class TypeResolver {
244
+ static class TypeResolver {
246
245
247
- private final ObjectReader objectReader = new ObjectMapper ().reader ();
246
+ // need a separate instance to bypass class hint checks
247
+ private final ObjectMapper mapper = new ObjectMapper ();
248
248
249
249
private final Supplier <TypeFactory > typeFactory ;
250
- private Supplier <String > hintName ;
250
+ private final Supplier <String > hintName ;
251
251
252
252
public TypeResolver (Supplier <TypeFactory > typeFactory , Supplier <String > hintName ) {
253
253
@@ -259,15 +259,13 @@ protected JavaType constructType(Class<?> type) {
259
259
return typeFactory .get ().constructType (type );
260
260
}
261
261
262
- protected JavaType resolveType (byte [] source , Class <?> type ) {
262
+ protected JavaType resolveType (byte [] source , Class <?> type ) throws IOException {
263
263
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?
264
+ Map <?, ?> map = mapper .readValue (source , Map .class );
265
+ Object typeName = map .get (hintName .get ());
266
+
267
+ if (typeName != null ) {
268
+ return typeFactory .get ().constructFromCanonical (typeName .toString ());
271
269
}
272
270
273
271
return constructType (type );
0 commit comments