Skip to content

[Bug Report] TypeResolver create a new ObjectMapper instance, ignoring custom configurations #3101

Closed as not planned
@swtalk

Description

@swtalk

📌 Summary

  • GenericJackson2JsonRedisSerializer.TypeResolver currently creates a new ObjectMapper instead of using an existing one.
  • This leads to issues where users cannot apply custom configurations like StreamReadConstraints for handling large JSON strings.
  • Even when explicitly setting ObjectMapper with maxStringLength = 100MB, the TypeResolver still enforces a 20MB limit, causing deserialization failures.

🔍 Steps to Reproduce

  1. Configure a custom ObjectMapper with StreamReadConstraints.maxStringLength = 100MB
  2. Use GenericJackson2JsonRedisSerializer for Redis serialization
  3. Store a large JSON object (e.g., 50MB) in Redis
  4. Attempt to retrieve the JSON using RedisTemplate or @Cacheable
  5. Observe that TypeResolver creates a new ObjectMapper, which applies a 20MB limit, leading to:
    Could not read JSON:String value length (20054016) exceeds the maximum allowed (20000000, from `StreamReadConstraints.getMaxStringLength()`)
    

🚀 Expected Behavior

  • TypeResolver should use the configured ObjectMapper instead of creating a new one internally.
  • Custom StreamReadConstraints.maxStringLength = 100MB should be respected when deserializing Redis data.

🛠 Suggested Fix

  • Modify TypeResolver to accept an ObjectMapper instance as a constructor argument.
  • If null, it should fall back to a default instance.

📎 Relevant Code (Current Issue)

static class TypeResolver {
    private final ObjectMapper mapper = new ObjectMapper(); // ⚠️ Always creates a new ObjectMapper
    private final Supplier<TypeFactory> typeFactory;
    private final Supplier<String> hintName;

    TypeResolver(Supplier<TypeFactory> typeFactory, Supplier<String> hintName) {
        this.typeFactory = typeFactory;
        this.hintName = hintName;
    }

    protected JavaType resolveType(byte[] source, Class<?> type) throws IOException {
        JsonNode root = mapper.readTree(source);
        JsonNode jsonNode = root.get(hintName.get());

        if (jsonNode instanceof TextNode && jsonNode.asText() != null) {
            return typeFactory.get().constructFromCanonical(jsonNode.asText());
        }
        return constructType(type);
    }
}

✅ Suggested Fix

private final ObjectMapper mapper;

TypeResolver(Supplier<TypeFactory> typeFactory, Supplier<String> hintName, ObjectMapper objectMapper) {
    this.typeFactory = typeFactory;
    this.hintName = hintName;
    this.mapper = (objectMapper != null) ? objectMapper : new ObjectMapper();
}

🌍 Environment

  • Spring Boot: 3.3.6
  • Jackson: 2.17.3
  • Spring Data Redis: Latest

📢 Additional Context

This issue causes Redis deserialization failures when handling large JSON payloads. Users configuring a custom ObjectMapper expect their settings to be respected, but TypeResolver currently overrides them by always creating a new instance. Fixing this will ensure better configurability and prevent unexpected SerializationException errors.


Metadata

Metadata

Assignees

Labels

status: supersededAn issue that has been superseded by another

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions