Skip to content

Vanilla BeanDeserializer fast path never used with default settings in 3.x (due to MapperFeature.DEFAULT_VIEW_INCLUSION default change) #6219

Open

Description

@cowtowncoder

(filed via Claude Code)

Describe the bug

In 3.x, the optimized "vanilla" processing of BeanDeserializer (_vanillaDeserialize()) is never used with default mapper settings -- even for plain POJOs with no @JsonView annotations, no injectables, no Object Id and default constructor.

Cause is BeanDeserializerBuilder._anyViews():

protected boolean _anyViews(Collection<SettableBeanProperty> props)
{
    // view processing must be enabled if:
    // (a) fields are not included by default (when deserializing with view), OR
    // (b) one of properties has view(s) to included in defined
    if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
        return true;
    }
    ...

With DEFAULT_VIEW_INCLUSION disabled, every bean deserializer gets _needViewProcesing = true, and since BeanDeserializerBase computes

_vanillaProcessing = !_nonStandardCreation
        && (_injectables == null)
        && !_needViewProcesing
        && (_objectIdReader == null);

_vanillaProcessing is always false. This logic predates 3.0, when the feature was enabled by default; but #1484 (via #4885) changed the default of DEFAULT_VIEW_INCLUSION to false for 3.0, so now all POJOs take the non-vanilla deserializeFromObject() path regardless of whether views are used at all.

Consequences:

  • Default configuration never uses the vanilla fast path, including improvements to it like Use JsonParser.nextNameMatchAndToken() in the vanilla BeanDeserializer loops #6193 (measured about +10% throughput for byte[] input -- but only with DEFAULT_VIEW_INCLUSION explicitly enabled).
  • Afterburner (SuperSonicBeanDeserializer, SuperSonicUnrolledDeserializer) and Blackbird (SuperSonicBeanDeserializer) check _vanillaProcessing and fall back to super.deserialize() when it is false, so their optimized deserialization appears to be bypassed with default settings as well.

Version Information

3.0.0 and later

Reproduction

For a plain POJO like

static class Point {
    public int x, y;
}

inspecting the root BeanDeserializer (e.g. via DeserializationContext.findRootValueDeserializer()):

  • default JsonMapper: _needViewProcesing = true, _vanillaProcessing = false
  • JsonMapper.builder().enable(MapperFeature.DEFAULT_VIEW_INCLUSION).build(): _needViewProcesing = false, _vanillaProcessing = true

(BeanDeserializerVanillaTest added by #6193 has to enable DEFAULT_VIEW_INCLUSION explicitly to get vanilla processing.)

Expected behavior

Vanilla processing should be usable whenever no view is active during deserialization (and no other non-vanilla conditions apply). Views only matter if a view is active, and that is already checked at runtime in non-vanilla paths, e.g. BeanDeserializer:

if (_needViewProcesing) {
    Class<?> view = ctxt.getActiveView();
    if (view != null) {
        return deserializeWithView(p, ctxt, bean, view);
    }
}

Possible approach: stop folding _needViewProcesing into _vanillaProcessing at construction time, and instead check !_needViewProcesing || ctxt.getActiveView() == null at the points where vanilla processing is chosen:

  • BeanDeserializerBase (flag computation and copy constructors)
  • BeanDeserializer.deserialize() / _deserializeOther()
  • BuilderBasedDeserializer
  • BeanAsArrayDeserializer / BeanAsArrayBuilderDeserializer
  • Afterburner / Blackbird SuperSonic* deserializers (jackson-modules-base)

(Note that simply changing _anyViews() would not be correct: with DEFAULT_VIEW_INCLUSION disabled, an active view excludes un-annotated properties, so view processing is needed whenever a view is active.)

Additional context

Found while reviewing #6193. Performance difference between vanilla and non-vanilla paths themselves has not yet been measured.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions