Skip to content

Forward Object Id references inside Collection properties leak Builder instance (follow-up to #1496) #5909

@cowtowncoder

Description

@cowtowncoder

Follow-up to #1496.

#1496 enabled Object Ids with Builder-based deserialization for the common cases and added clear error reporting (InvalidDefinitionException) for forward Object Id references bound to scalar properties, where the reference would otherwise be silently resolved against the discarded Builder instance.

One case remains broken: forward Object Id references that appear as items of a Collection-typed property. The collection-item path does not go through ObjectIdReferenceProperty.deserializeSetAndReturn, so DeserializationContext.addPendingForwardRef is never called and the guard in BuilderBasedDeserializer.finishBuild (hasPendingForwardRefsFor) does not trip. As a result:

  • No error is reported.
  • The unresolved slot ends up holding the Builder instance instead of the built target object after resolution.

Reproduction

See src/test/java/tools/jackson/databind/tofix/ObjectIdWithBuilder1496Test.java (forwardReferenceInCollection, currently @JacksonTestFailureExpected):

{"entities":[
  {"id":1,"refs":[2]},
  {"id":2,"ref":1,"refs":[]}
]}

Entity uses @JsonIdentityInfo + @JsonDeserialize(builder = EntityBuilder.class). After deserialization, entities[0].refs[0] is an EntityBuilder rather than the built Entity with id 2.

expected: Entity@... but was: EntityBuilder@...

Suggested fix direction

Two options, both requiring changes beyond the scope of #1496:

  1. Plumb collection-item forward references through addPendingForwardRef so the existing BuilderBasedDeserializer.finishBuild guard catches them and emits the same InvalidDefinitionException ("Cannot resolve forward Object Id references for Builder-based type …").
  2. Actually support the case: re-bind collection-item references from the builder to the built object when updateObjectId is called in finishBuild. This would likely require the collection Referring implementation to late-bind via the ReadableObjectId entry after updateObjectId rewrites the bound item.

Option 1 matches the existing fail-fast contract for the scalar case and is the minimum acceptable fix. Option 2 is the full solution.

Version

3.x (observed on current 3.x branch after #1496 / commit 476f3d9f5).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions