[Home](../README.md) | Prev: [String template and two keyspaces](03-string-template-and-two-keyspaces.md) | Next: [@RedisHash](05-redis-hash.md) # 4. JSON serializers: readable values, Jackson 3 The usual fix for unreadable values is JSON. Spring Data Redis 4.1 has two families of JSON serializer, and the names are easy to mix up ([16-serializers-javap.txt](output/16-serializers-javap.txt), read from the jar): | Class | Jackson | Status | |---|---|---| | `GenericJackson2JsonRedisSerializer` | 2 | `@Deprecated(since = "4.0")` | | `Jackson2JsonRedisSerializer` | 2 | `@Deprecated(since = "4.0")` | | `GenericJacksonJsonRedisSerializer` | 3 (`tools.jackson`) | not deprecated | | `JacksonJsonRedisSerializer` | 3 (`tools.jackson`) | not deprecated | `Jackson` in the name means Jackson 3. `Jackson2` means Jackson 2, and it is what most examples on the web still show. The Redis starter includes neither ([chapter 1](01-what-boot-gives-you.md)); add `spring-boot-starter-jackson` for Jackson 3. ## Typed: JacksonJsonRedisSerializer<T> One value type per template, plain JSON, nothing extra ([06-jackson-serializers.txt](output/06-jackson-serializers.txt)): ``` $ redis-cli -p 6390 GET user:1 {"id":1,"name":"Ankur"} ``` The constructors are `(Class)`, `(JavaType)`, `(ObjectMapper, Class)` and so on. The cost is one template bean per value type. [userRedisTemplate](../src/main/java/com/ankurm/redis/config/JsonRedisConfig.java) is the example. ## Generic: GenericJacksonJsonRedisSerializer One template for any value type. To read a value back as the right class it has to know the class, so it needs *default typing*, which writes a type property into every value: ``` $ redis-cli -p 6390 GET user:2 {"@class":"com.ankurm.redis.model.User","id":2,"name":"Ankur"} ``` Without typing configured (`GenericJacksonJsonRedisSerializer.builder().build()`), the JSON has no `@class`, and reading it back returns a `LinkedHashMap`, not a `User`: ``` read back: {id=3, name=Ankur} (LinkedHashMap) ``` The builder offers `enableDefaultTyping(PolymorphicTypeValidator)`, `enableUnsafeDefaultTyping()`, `typeValidator(...)`, `typePropertyName(...)`, `customize(Consumer)` and more. ## The `@class` property is input Whatever is in Redis chooses the class the serializer instantiates. Anyone who can write to that Redis (another service, an operator, an attacker with network access to an unauthenticated instance) can write `{"@class": ...}` for any class on your classpath. That is why the builder has a method called `enableUnsafeDefaultTyping` and why `enableDefaultTyping` takes a validator. [JsonRedisConfig](../src/main/java/com/ankurm/redis/config/JsonRedisConfig.java) allows only subtypes under `com.ankurm.redis.`, and [07-jackson-untrusted-class.txt](output/07-jackson-untrusted-class.txt) shows what happens to a class outside it: ``` $ redis-cli -p 6390 SET user:evil '{"@class":"com.ankurm.other.Outsider","name":"x"}' OK ... message: Could not read JSON: Could not resolve type id 'com.ankurm.other.Outsider' as a subtype of `com.ankurm.other.Outsider`: Configured `PolymorphicTypeValidator` (of type `tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator`) denied resolution ``` The same transcript shows the other failure you will meet: a class inside the allow-list that no longer exists (renamed, moved) fails with `Cannot locate class`. Type names in Redis are a compatibility surface, exactly as the class name inside a JDK-serialised value was. ## Which one? | | Typed | Generic | |---|---|---| | redis-cli output | plain JSON | JSON with `@class` | | Other languages can read it | yes | yes, if they ignore `@class` | | Rename or move the class | safe | breaks reads | | Needs an allow-list | no | **yes** | | Templates needed | one per type | one | If a Redis is shared between services, prefer typed, or configure `typePropertyName` and an allow-list deliberately. If it is private to one application, generic with a validator is fine. ## Going deeper - [JsonRedisConfig](../src/main/java/com/ankurm/redis/config/JsonRedisConfig.java) - [JacksonSerializerTest](../src/test/java/com/ankurm/redis/JacksonSerializerTest.java) - [Spring Data Redis: serializers](https://docs.spring.io/spring-data/redis/reference/redis/template.html#redis:serializer) - [Jackson 2 to Jackson 3 migration guide](https://ankurm.com/jackson-3-migration-guide/) [Home](../README.md) | Prev: [String template and two keyspaces](03-string-template-and-two-keyspaces.md) | Next: [@RedisHash](05-redis-hash.md)