FasterXML / FasterXML/jackson-databind

Support conditional type ID inclusion during deserialization

Open
#6,147 1 comment 0 reactions 0 assignees View on GitHub
to-evaluate
Dominant language
Java
Stars
3.7k
Forks
1.5k
Avg merge
3d 6h
Merged PRs (30d)
28

Description

### Is your feature request related to a problem? Please describe.

When a default implementation is used on a polymorphic `@JsonTypeInfo` the `visible = true` attribute needs to be set for that default implementation to know the missing type. However for the non-default cases usually the type is known ahead of time, and making the `type` property visible to the concrete deserializer adds significant allocation cost for the intermediate `TokenBuffer`.

Specifically, [this](https://github.com/FasterXML/jackson-databind/blob/7b3ed3244e7a2b9276daced7eaf9dd28357f420c/src/main/java/tools/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java#L131-L143) code snippet allocates a token buffer (with a 16 element segment), the associated `TokenBuffer$Parser`, and `JsonParserSequence`.

### Describe the solution you'd like

Ideally, adding a third option to the `JsonTypeInfo.visible` field that indicates "visible only to the default implementation", but this is likely fairly complicated due to the field currently being a boolean.

Alternatively, perhaps adding an optimized `JsonParser` subtype like `PrependedTypeProperty(String typeField, String typeId, JsonParser delegate)` that avoids the majority of the allocations.

Currently, we're using our own subtype of `AsPropertyTypeDeserializer` that overrides `_deserializeTypedForId` that overrides `if (_typeIdVisible) {` to instead check `if (deserializer == _defaultImplDeserializer) {` in a few targeted places we've observed this performance issue, but this is not ideal because those are internal APIs. If modifying `@JsonTypeInfo` is too high lift, perhaps adding the option to the public API of `AsPropertyTypeDeserializer` or `StdTypeResolverBuilder` would be an alternative.

### Performance Test

```java
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type",
visible = true,
defaultImpl = Default.class)
@JsonSubTypes(@JsonSubTypes.Type(value = Concrete.class, name = "concrete"))
private interface Base {}

private record Default(String type) implements Base {}

@JsonTypeName("concrete")
@JsonPropertyOrder({"type", "content"})
private static final class Concrete implements Base {
@JsonProperty("type")
public String type() {
return "concrete";
}

@JsonProperty("content")
public int content;
}

public static void main(String[] args) throws IOException {
ObjectMapper objectMapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Concrete obj = new Concrete();
obj.content = 12345;
byte[] bytes = objectMapper.writeValueAsBytes(
IntStream.range(0, 1000).mapToObj(_x -> obj).toList());
TypeReference> type = new TypeReference<>() {};
for (int i = 0; i < 10_000; i++) {
objectMapper.readValue(bytes, type);
}
}
```

Image

### Additional context

_No response_

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with AsPropertyTypeDeserializer, especially _deserializeTypedForId and the referenced lines 131-143, then review JsonTypeInfo.visible and the public AsPropertyTypeDeserializer or StdTypeResolverBuilder APIs. Reproduce the supplied performance test and verify that default implementations can receive the type while non-default deserializers avoid the unnecessary intermediate allocations.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.