diff --git a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java index 23b513be1..16a298b06 100644 --- a/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java +++ b/src/main/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializer.java @@ -25,7 +25,6 @@ import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; import com.fasterxml.jackson.databind.SerializerProvider; @@ -37,6 +36,9 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * Generic Jackson 2-based {@link RedisSerializer} that maps {@link Object objects} to JSON using dynamic typing. + *
+ * JSON reading and writing can be customized by configuring {@link JacksonObjectReader} respective
+ * {@link JacksonObjectWriter}.
*
* @author Christoph Strobl
* @author Mark Paluch
@@ -47,6 +49,10 @@ public class GenericJackson2JsonRedisSerializer implements RedisSerializer
- * This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances.
+ * This serializer can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances.
* Note:Null objects are serialized as empty arrays and vice versa.
+ *
+ * JSON reading and writing can be customized by configuring {@link JacksonObjectReader} respective
+ * {@link JacksonObjectWriter}.
*
* @author Thomas Darimont
+ * @author Mark Paluch
* @since 1.2
*/
public class Jackson2JsonRedisSerializer
diff --git a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java
new file mode 100644
index 000000000..46552db37
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.serializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Defines the contract for Object Mapping readers. Implementations of this interface can deserialize a given byte array
+ * holding JSON to an Object considering the target type.
+ *
+ * Reader functions can customize how the actual JSON is being deserialized by e.g. obtaining a customized
+ * {@link com.fasterxml.jackson.databind.ObjectReader} applying serialization features, date formats, or views.
+ *
+ * @author Mark Paluch
+ * @since 3.0
+ */
+@FunctionalInterface
+public interface JacksonObjectReader {
+
+ /**
+ * Read an object graph from the given root JSON into a Java object considering the {@link JavaType}.
+ *
+ * @param mapper the object mapper to use.
+ * @param source the JSON to deserialize.
+ * @param type the Java target type
+ * @return the deserialized Java object.
+ * @throws IOException if an I/O error or JSON deserialization error occurs.
+ */
+ Object read(ObjectMapper mapper, byte[] source, JavaType type) throws IOException;
+
+ /**
+ * Create a default {@link JacksonObjectReader} delegating to {@link ObjectMapper#readValue(InputStream, JavaType)}.
+ *
+ * @return the default {@link JacksonObjectReader}.
+ */
+ static JacksonObjectReader create() {
+ return (mapper, source, type) -> mapper.readValue(source, 0, source.length, type);
+ }
+
+}
diff --git a/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java
new file mode 100644
index 000000000..64ecd44de
--- /dev/null
+++ b/src/main/java/org/springframework/data/redis/serializer/JacksonObjectWriter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.redis.serializer;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Defines the contract for Object Mapping writers. Implementations of this interface can serialize a given Object to a
+ * {@code byte[]} containing JSON.
+ *
+ * Writer functions can customize how the actual JSON is being written by e.g. obtaining a customized
+ * {@link com.fasterxml.jackson.databind.ObjectWriter} applying serialization features, date formats, or views.
+ *
+ * @author Mark Paluch
+ * @since 3.0
+ */
+@FunctionalInterface
+public interface JacksonObjectWriter {
+
+ /**
+ * Write the object graph with the given root {@code source} as byte array.
+ *
+ * @param mapper the object mapper to use.
+ * @param source the root of the object graph to marshal.
+ * @return a byte array containing the serialized object graph.
+ * @throws IOException if an I/O error or JSON serialization error occurs.
+ */
+ byte[] write(ObjectMapper mapper, Object source) throws IOException;
+
+ /**
+ * Create a default {@link JacksonObjectWriter} delegating to {@link ObjectMapper#writeValueAsBytes(Object)}.
+ *
+ * @return the default {@link JacksonObjectWriter}.
+ */
+ static JacksonObjectWriter create() {
+ return ObjectMapper::writeValueAsBytes;
+ }
+
+}
diff --git a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java
index f13d7cf1a..9535e7512 100644
--- a/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/serializer/GenericJackson2JsonRedisSerializerUnitTests.java
@@ -26,11 +26,13 @@ import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
+
import org.springframework.beans.BeanUtils;
import org.springframework.cache.support.NullValue;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
@@ -162,6 +164,24 @@ class GenericJackson2JsonRedisSerializerUnitTests {
assertThat(serializer.deserialize(serializer.serialize(source))).isEqualTo(source);
}
+ @Test // GH-2322
+ void shouldConsiderWriter() {
+
+ User user = new User();
+ user.email = "walter@heisenberg.com";
+ user.id = 42;
+ user.name = "Walter White";
+
+ GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer((String) null,
+ JacksonObjectReader.create(), (mapper, source) -> {
+ return mapper.writerWithView(Views.Basic.class).writeValueAsBytes(source);
+ });
+
+ byte[] result = serializer.serialize(user);
+
+ assertThat(new String(result)).contains("id").contains("name").doesNotContain("email");
+ }
+
private static void serializeAndDeserializeNullValue(GenericJackson2JsonRedisSerializer serializer) {
NullValue nv = BeanUtils.instantiateClass(NullValue.class);
@@ -252,4 +272,18 @@ class GenericJackson2JsonRedisSerializerUnitTests {
}
}
+ public class User {
+ @JsonView(Views.Basic.class) public int id;
+ @JsonView(Views.Basic.class) public String name;
+ @JsonView(Views.Detailed.class) public String email;
+ @JsonView(Views.Detailed.class) public String mobile;
+ }
+
+ public class Views {
+ interface Basic {}
+
+ interface Detailed {}
+
+ }
+
}
diff --git a/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java b/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java
index f0d57830c..37982125a 100644
--- a/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java
+++ b/src/test/java/org/springframework/data/redis/serializer/Jackson2JsonRedisSerializerTests.java
@@ -25,8 +25,11 @@ import org.springframework.data.redis.Person;
import org.springframework.data.redis.PersonObjectFactory;
/**
+ * Unit tests for {@link Jackson2JsonRedisSerializer}.
+ *
* @author Thomas Darimont
* @author Christoph Strobl
+ * @author Mark Paluch
*/
class Jackson2JsonRedisSerializerTests {
@@ -64,4 +67,12 @@ class Jackson2JsonRedisSerializerTests {
assertThatIllegalArgumentException().isThrownBy(() -> serializer.setObjectMapper(null));
}
+ @Test // GH-2322
+ void shouldConsiderWriter() {
+
+ Person person = new PersonObjectFactory().instance();
+ serializer.setWriter((mapper, source) -> "foo".getBytes());
+ assertThat(serializer.serialize(person)).isEqualTo("foo".getBytes());
+ }
+
}