Allow configuring custom NullValueSerializer.

Closes #2878
Original pull request: #2905
This commit is contained in:
annemayor
2024-05-11 22:48:55 +09:00
committed by Mark Paluch
parent ea84629bac
commit 33326aa2db
2 changed files with 124 additions and 3 deletions

View File

@@ -134,6 +134,17 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
this.mapper.setDefaultTyping(typer);
}
/**
* Factory method returning a {@literal Builder} used to construct and configure a {@link GenericJackson2JsonRedisSerializer}.
*
* @return new {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
* @since 3.3
*/
public static GenericJackson2JsonRedisSerializerBuilder builder(ObjectMapper objectMapper, JacksonObjectReader reader,
JacksonObjectWriter writer) {
return new GenericJackson2JsonRedisSerializerBuilder(objectMapper, reader, writer);
}
/**
* Setting a custom-configured {@link ObjectMapper} is one way to take further control of the JSON serialization
* process. For example, an extended {@link SerializerFactory} can be configured that provides custom serializers for
@@ -396,6 +407,69 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer<Objec
}
}
/**
* {@literal Builder} for creating a {@link GenericJackson2JsonRedisSerializer}.
*
* @author Anne Lee
* @since 3.3
*/
public static class GenericJackson2JsonRedisSerializerBuilder {
@Nullable
private String classPropertyTypeName;
private JacksonObjectReader reader;
private JacksonObjectWriter writer;
private ObjectMapper mapper;
@Nullable
private StdSerializer<NullValue> nullValueSerializer;
private GenericJackson2JsonRedisSerializerBuilder(
ObjectMapper objectMapper,
JacksonObjectReader reader,
JacksonObjectWriter writer
) {
this.mapper = objectMapper;
this.reader = reader;
this.writer = writer;
}
/**
* Configure a classPropertyName.
*
* @param classPropertyTypeName can be {@literal null}.
* @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
* @since 3.3
*/
public GenericJackson2JsonRedisSerializerBuilder classPropertyTypeName(@Nullable String classPropertyTypeName) {
this.classPropertyTypeName = classPropertyTypeName;
return this;
}
/**
* Register a nullValueSerializer.
*
* @param nullValueSerializer the {@link StdSerializer} to use for {@link NullValue} serialization. Can be {@literal null}.
* @return this {@link GenericJackson2JsonRedisSerializer.GenericJackson2JsonRedisSerializerBuilder}.
*/
public GenericJackson2JsonRedisSerializerBuilder registerNullValueSerializer(@Nullable StdSerializer<NullValue> nullValueSerializer) {
this.nullValueSerializer = nullValueSerializer;
return this;
}
/**
* Create new instance of {@link GenericJackson2JsonRedisSerializer} with configuration options applied.
*
* @return new instance of {@link GenericJackson2JsonRedisSerializer}.
*/
public GenericJackson2JsonRedisSerializer build() {
Assert.notNull(this.mapper, "ObjectMapper must not be null");
Assert.notNull(this.reader, "Reader must not be null");
Assert.notNull(this.writer, "Writer must not be null");
this.mapper.registerModule(new SimpleModule().addSerializer(this.nullValueSerializer != null ? this.nullValueSerializer : new NullValueSerializer(this.classPropertyTypeName)));
return new GenericJackson2JsonRedisSerializer(this.mapper, this.reader, this.writer, this.classPropertyTypeName);
}
}
/**
* Custom {@link StdTypeResolverBuilder} that considers typing for non-primitive types. Primitives, their wrappers and
* primitive arrays do not require type hints. The default {@code DefaultTyping#EVERYTHING} typing does not satisfy

View File

@@ -40,6 +40,10 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -420,7 +424,7 @@ class GenericJackson2JsonRedisSerializerUnitTests {
}
@Test // GH-2601
public void internalObjectMapperCustomization() {
void internalObjectMapperCustomization() {
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
@@ -432,13 +436,13 @@ class GenericJackson2JsonRedisSerializerUnitTests {
assertThat(serializer.configure(configurer)).isSameAs(serializer);
verify(mockObjectMapper, times(1)).registerModule(eq(mockModule));
verify(mockObjectMapper, times(1)).registerModule(mockModule);
verifyNoMoreInteractions(mockObjectMapper);
verifyNoInteractions(mockModule);
}
@Test // GH-2601
public void configureWithNullConsumerThrowsIllegalArgumentException() {
void configureWithNullConsumerThrowsIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new GenericJackson2JsonRedisSerializer().configure(null))
@@ -446,6 +450,49 @@ class GenericJackson2JsonRedisSerializerUnitTests {
.withNoCause();
}
@Test
void defaultSerializeAndDeserializeNullValueWithBuilderClass() {
GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder(
new ObjectMapper().enableDefaultTyping(DefaultTyping.EVERYTHING, As.PROPERTY),
JacksonObjectReader.create(), JacksonObjectWriter.create())
.classPropertyTypeName(null)
.registerNullValueSerializer(null)
.build();
serializeAndDeserializeNullValue(serializer);
}
@Test
void customSerializeAndDeserializeNullValueWithBuilderClass() {
GenericJackson2JsonRedisSerializer serializer = GenericJackson2JsonRedisSerializer.builder(
new ObjectMapper(), JacksonObjectReader.create(), JacksonObjectWriter.create())
.classPropertyTypeName(null)
.registerNullValueSerializer(new StdSerializer<>(NullValue.class) {
@Override
public void serialize(NullValue nullValue,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeNull();
}
@Override
public void serializeWithType(NullValue value, JsonGenerator jsonGenerator, SerializerProvider serializers,
TypeSerializer typeSerializer) throws IOException {
serialize(value, jsonGenerator, serializers);
}
})
.build();
NullValue nv = BeanUtils.instantiateClass(NullValue.class);
byte[] serializedValue = serializer.serialize(nv);
assertThat(serializedValue).isNotNull();
Object deserializedValue = serializer.deserialize(serializedValue);
assertThat(deserializedValue).isNull();
}
private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) {
NullValue nv = BeanUtils.instantiateClass(NullValue.class);