Skip to content

Fix field projection collapsing nested JsonPointers to leaf names#183

Merged
vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/openidm-183
Jun 10, 2026
Merged

Fix field projection collapsing nested JsonPointers to leaf names#183
vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issues/openidm-183

Conversation

@vharseko

Copy link
Copy Markdown
Member

Summary

Resources.filterResource(JsonValue, Collection<JsonPointer>) built the
projected result using field.leaf() as a flat key:

final String key = field.leaf();
filtered.put(key, value.getObject());

As a result, a nested pointer (e.g. manager/userName) lost its nesting and
was stored under the leaf name (userName), overwriting the same-named
top-level field. With _fields=userName,manager,manager/userName, the
top-level userName was replaced by the value of manager/userName.

This affected any RequestHandler that does not set the response fields
explicitly (e.g. custom scripted OpenIDM endpoints), because the projection is
performed in InternalConnection via
Resources.filterResource(response, request.getFields()).

Changes

  • Resources.filterResource(JsonValue, Collection<JsonPointer>) now uses
    JsonValue.putPermissive(field, value) so each requested JsonPointer is
    written back under its full path, preserving nesting. Missing parent objects
    are created on demand, and same-named leaf fields at different nesting levels
    no longer collide.
    • The empty pointer (field.isEmpty()) still copies all resource fields.
    • Shallow-copy semantics are preserved (value.getObject()).
    • Unresolved pointers (including array-index pointers that do not resolve)
      are skipped, so there is no regression for that case.
  • Javadoc updated to document that the projection preserves the nested
    structure of the requested fields and that same-named leaf fields at
    different levels do not conflict.
  • Tests in ResourcesTest:
    • Updated testFilterData rows that encoded the old flat behavior to the
      correct nested structure.
    • Added a dedicated test
      (testFilterPreservesNestedFieldsWithCollidingLeafNames) and a data row
      covering the leaf-name collision:
      fields = [userName, manager, manager/userName] over
      { userName: "bjensen", manager: { userName: "jdoe" } }
      must yield result.userName == "bjensen" and
      result.manager.userName == "jdoe".

Behavior

Before:

{ "userName": "jdoe", "manager": { "userName": "jdoe" } }

After:

{ "userName": "bjensen", "manager": { "userName": "jdoe" } }

Testing

mvn -o test -Dtest=ResourcesTest in commons/rest/json-resource — all 106
tests pass.

Related

Once an updated commons 3.1.1-SNAPSHOT is published, the OpenIDM test
openidm-script/.../CustomEndpointFieldProjectionTest will pass.

See OpenIDM discussion:
OpenIdentityPlatform/OpenIDM#183

Resources.filterResource(JsonValue, Collection<JsonPointer>) built the
projection using field.leaf() as a flat key, so a nested pointer such as
"manager/userName" lost its nesting and overwrote the same-named top-level
field. Requesting _fields=userName,manager,manager/userName caused the
top-level userName to be replaced by manager/userName.

Use JsonValue.putPermissive(field, value) so each requested pointer is written
back under its full path, preserving nesting and creating parent objects on
demand. Same-named leaf fields at different nesting levels no longer collide.
The empty pointer still copies all fields, shallow-copy semantics are kept, and
unresolved (e.g. array-index) pointers are skipped without regression.

Update the Javadoc to document the nesting-preserving behavior and extend
ResourcesTest with a leaf-name collision case
([userName, manager, manager/userName] over
{ userName: "bjensen", manager: { userName: "jdoe" } }).

Refs: OpenIdentityPlatform/OpenIDM#183
@vharseko vharseko merged commit 3bb9a67 into OpenIdentityPlatform:master Jun 10, 2026
15 checks passed
@vharseko vharseko deleted the issues/openidm-183 branch June 10, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants