Skip to content

Commit 38d4e7d

Browse files
Switch jackson default mapping default from NON_FINAL to EVERYTHING.
1 parent 31ea069 commit 38d4e7d

File tree

4 files changed

+99
-9
lines changed

4 files changed

+99
-9
lines changed

src/main/java/org/springframework/data/redis/hash/Jackson2HashMapper.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.redis.hash;
1717

18+
import static com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.*;
19+
1820
import java.io.IOException;
1921
import java.text.ParseException;
2022
import java.util.ArrayList;
@@ -39,8 +41,10 @@
3941
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
4042
import com.fasterxml.jackson.core.JsonGenerator;
4143
import com.fasterxml.jackson.core.JsonParser;
44+
import com.fasterxml.jackson.core.TreeNode;
4245
import com.fasterxml.jackson.databind.DeserializationContext;
4346
import com.fasterxml.jackson.databind.DeserializationFeature;
47+
import com.fasterxml.jackson.databind.JavaType;
4448
import com.fasterxml.jackson.databind.JsonDeserializer;
4549
import com.fasterxml.jackson.databind.JsonNode;
4650
import com.fasterxml.jackson.databind.JsonSerializer;
@@ -49,7 +53,9 @@
4953
import com.fasterxml.jackson.databind.SerializationFeature;
5054
import com.fasterxml.jackson.databind.SerializerProvider;
5155
import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
56+
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
5257
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
58+
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
5359
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
5460
import com.fasterxml.jackson.databind.module.SimpleModule;
5561
import com.fasterxml.jackson.databind.ser.std.CalendarSerializer;
@@ -157,9 +163,30 @@ public class Jackson2HashMapper implements HashMapper<Object, String, Object> {
157163
*/
158164
public Jackson2HashMapper(boolean flatten) {
159165

160-
this(new ObjectMapper().findAndRegisterModules(), flatten);
166+
this(new ObjectMapper() {
167+
168+
@Override
169+
protected TypeResolverBuilder<?> _constructDefaultTypeResolverBuilder(DefaultTyping applicability,
170+
PolymorphicTypeValidator ptv) {
171+
return new DefaultTypeResolverBuilder(applicability, ptv) {
172+
public boolean useForType(JavaType t) {
173+
174+
if (t.isPrimitive()) {
175+
return false;
176+
}
177+
178+
if (EVERYTHING.equals(_appliesFor)) {
179+
return !TreeNode.class.isAssignableFrom(t.getRawClass());
180+
}
181+
182+
return super.useForType(t);
183+
}
184+
};
185+
}
186+
}.findAndRegisterModules(), flatten);
161187

162-
typingMapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
188+
typingMapper.activateDefaultTyping(typingMapper.getPolymorphicTypeValidator(), DefaultTyping.EVERYTHING,
189+
As.PROPERTY);
163190
typingMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
164191

165192
// Prevent splitting time types into arrays. E

src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.redis.serializer;
1717

18+
import static com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping.*;
19+
1820
import java.io.IOException;
1921

2022
import org.springframework.cache.support.NullValue;
@@ -26,10 +28,14 @@
2628
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
2729
import com.fasterxml.jackson.core.JsonGenerator;
2830
import com.fasterxml.jackson.core.JsonProcessingException;
31+
import com.fasterxml.jackson.core.TreeNode;
32+
import com.fasterxml.jackson.databind.JavaType;
2933
import com.fasterxml.jackson.databind.ObjectMapper;
3034
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
3135
import com.fasterxml.jackson.databind.SerializerProvider;
3236
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
37+
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
38+
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
3339
import com.fasterxml.jackson.databind.module.SimpleModule;
3440
import com.fasterxml.jackson.databind.ser.SerializerFactory;
3541
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
@@ -64,17 +70,37 @@ public GenericJackson2JsonRedisSerializer() {
6470
*/
6571
public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName) {
6672

67-
this(new ObjectMapper());
73+
this(new ObjectMapper() {
74+
75+
@Override
76+
protected TypeResolverBuilder<?> _constructDefaultTypeResolverBuilder(DefaultTyping applicability,
77+
PolymorphicTypeValidator ptv) {
78+
return new DefaultTypeResolverBuilder(applicability, ptv) {
79+
public boolean useForType(JavaType t) {
80+
81+
if (t.isPrimitive()) {
82+
return false;
83+
}
84+
85+
if (EVERYTHING.equals(_appliesFor)) {
86+
return !TreeNode.class.isAssignableFrom(t.getRawClass());
87+
}
88+
89+
return super.useForType(t);
90+
}
91+
};
92+
}
93+
});
6894

6995
// simply setting {@code mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)} does not help here since we need
7096
// the type hint embedded for deserialization using the default typing feature.
7197
registerNullValueSerializer(mapper, classPropertyTypeName);
7298

7399
if (StringUtils.hasText(classPropertyTypeName)) {
74-
mapper.activateDefaultTypingAsProperty(mapper.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL,
100+
mapper.activateDefaultTypingAsProperty(mapper.getPolymorphicTypeValidator(), EVERYTHING,
75101
classPropertyTypeName);
76102
} else {
77-
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), DefaultTyping.NON_FINAL, As.PROPERTY);
103+
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), EVERYTHING, As.PROPERTY);
78104
}
79105
}
80106

@@ -183,12 +209,17 @@ private static class NullValueSerializer extends StdSerializer<NullValue> {
183209
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
184210
*/
185211
@Override
186-
public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider)
187-
throws IOException {
212+
public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
188213

189214
jgen.writeStartObject();
190215
jgen.writeStringField(classIdentifier, NullValue.class.getName());
191216
jgen.writeEndObject();
192217
}
218+
219+
@Override
220+
public void serializeWithType(NullValue value, JsonGenerator gen, SerializerProvider serializers,
221+
TypeSerializer typeSer) throws IOException {
222+
serialize(value, gen, serializers);
223+
}
193224
}
194225
}

src/test/java/org/springframework/data/redis/mapping/Jackson2HashMapperUnitTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,15 @@ void dateValueShouldBeTreatedCorrectly() {
183183
assertBackAndForwardMapping(source);
184184
}
185185

186+
@Test // GH-1566
187+
void mapFinalClass() {
188+
189+
MeFinal source = new MeFinal();
190+
source.value = "id-1";
191+
192+
assertBackAndForwardMapping(source);
193+
}
194+
186195
@Data
187196
public static class WithList {
188197
List<String> strings;
@@ -206,4 +215,9 @@ private static class WithDates {
206215
private LocalDate localDate;
207216
private LocalDateTime localDateTime;
208217
}
218+
219+
@Data
220+
public static final class MeFinal {
221+
private String value;
222+
}
209223
}

src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222

2323
import java.io.IOException;
2424

25+
import lombok.Data;
2526
import org.junit.jupiter.api.Test;
2627
import org.mockito.Mockito;
27-
2828
import org.springframework.beans.BeanUtils;
2929
import org.springframework.cache.support.NullValue;
3030

@@ -141,14 +141,26 @@ void shouldSerializeNullValueSoThatItCanBeDeserializedWithDefaultTypingEnabled()
141141
void shouldSerializeNullValueWithCustomObjectMapper() {
142142

143143
ObjectMapper mapper = new ObjectMapper();
144-
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
144+
mapper.enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY);
145145

146146
GenericJackson2JsonRedisSerializer.registerNullValueSerializer(mapper, null);
147147
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(mapper);
148148

149149
serializeAndDeserializeNullValue(serializer);
150150
}
151151

152+
@Test // GH-1566
153+
void deserializeShouldBeAbleToRestoreFinalObjectAfterSerialization() {
154+
155+
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
156+
157+
FinalObject source = new FinalObject();
158+
source.longValue=1L;
159+
source.simpleObject = new SimpleObject(2L);
160+
161+
assertThat(serializer.deserialize(serializer.serialize(source))).isEqualTo(source);
162+
}
163+
152164
private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) {
153165

154166
NullValue nv = BeanUtils.instantiateClass(NullValue.class);
@@ -201,6 +213,12 @@ public boolean equals(Object obj) {
201213

202214
}
203215

216+
@Data
217+
static final class FinalObject {
218+
public Long longValue;
219+
SimpleObject simpleObject;
220+
}
221+
204222
static class SimpleObject {
205223

206224
public Long longValue;

0 commit comments

Comments
 (0)