Skip to content

Chore: Adopt SJSF omitExtraData algorithm#5065

Open
heath-freenome wants to merge 2 commits into
mainfrom
adopt-SJSF-omitExtraData-algorithm
Open

Chore: Adopt SJSF omitExtraData algorithm#5065
heath-freenome wants to merge 2 commits into
mainfrom
adopt-SJSF-omitExtraData-algorithm

Conversation

@heath-freenome
Copy link
Copy Markdown
Member

Reasons for making this change

Adopted the omitExtraData() processing algorithm from svelte-jsonschema-form, adding in the removeOptionalEmptyObject() behavior

  • In @rjsf/utils:
    • Rewrote omitExtraData() basing it on the algorithm from SJSF, with improvements to pick up the removeOptionalEmptyObject() behavior
    • Refactored the isValueEmpty() function from removeOptionalEmptyObject() for use by omitExtraData() exporting it from utils
    • Deprecated the removeOptionalEmptyObject() and toPathSchema() functions, toPathSchema() on SchemaUtilsType and the PathSchema type
    • Updated the tests for toPathSchema() to add missing coverage after the rewrite
    • Updated the tests for omitExtraData() to cover isValueEmpty(), the ported removeOptionalEmptyObject() behavior and keeping coverage 100%
  • In @rjsf/core:
    • Updated Form to stop using removeOptionalEmptyObjects() and deprecating removeEmptyOptionalObjects prop.
    • Added tests for Form to cover the remove empty objects cases
  • Updated the form-props.md and utility-functions.md to document the deprecations
  • Updated the uiSchema.md to improve the enumNames documents slightly
  • Updated the CHANGELOG.md accordingly

Checklist

  • I'm updating documentation
  • I'm adding or updating code
    • I've added and/or updated tests. I've run npx nx run-many --target=build --exclude=@rjsf/docs && npm run test:update to update snapshots, if needed.
    • I've updated docs if needed
    • I've updated the changelog with a description of the PR
  • I'm adding a new feature
    • I've updated the playground with an example use of the feature

@heath-freenome
Copy link
Copy Markdown
Member Author

@x0k Per your suggestion, I've adapted your code to this library. Feel free to take a look

Copy link
Copy Markdown
Contributor

@x0k x0k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case, I’m attaching my implementation of removeOptionalEmptyObject, which has virtually no overhead PR + fix.

new RegExp(pattern),
schemaDef as S | boolean,
]);
const knownProperties = new Set(Object.keys(properties ?? {}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing getKnownProperties will result in bar being preserved in the following case (if this was intentional, it would make sense to add this test to the test suite)

const schema: RJSFSchema = {
  type: 'object',
  oneOf: [
    {
      properties: {
        kind: { const: 'a' },
        foo: { type: 'string' },
      },
    },
    {
      properties: {
        kind: { const: 'b' },
        bar: { type: 'string' },
      },
    },
  ],
  additionalProperties: {
    type: 'string',
  },
};

const value = {
  kind: 'a',
  bar: 'hello',
  extra: 'keep me',
};

const result = omitExtraData(testValidator, schema, schema, value);

expect(result).toEqual({
  kind: 'a',
  bar: 'hello',
  extra: 'keep me',
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test was added

Comment on lines +107 to +109
if (isObject(value)) {
return Object.values(value as GenericObjectType).every(isValueEmpty);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should not be recursive, or setProperty should call a different function. Since omitExtraData performs a depth-first traversal, empty objects could be collapsed when returning the result from omit, for example. It may even make sense to integrate all isValueEmpty logic directly into omit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated setProperty() per your recommendation

Comment on lines +334 to +339
const options = (oneOf as Array<S | boolean>).map((d) => {
if (!isSchemaObj(d)) {
return (d ? {} : { not: {} }) as S;
}
return d.additionalProperties === false ? ({ ...d, additionalProperties: true } as S) : d;
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it looks like this will not work with precompiled validators.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same limitation that already exists elsewhere in the codebase (e.g. MultiSchemaField) when getClosestMatchingOption is used with dynamic schemas

I wasn’t aware of this. Perhaps precompile module (usage) could also be adapted at some point.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@heath-freenome did the prior omitExtraData handle (or not handle) oneOf matching in a way that worked for precompiled validators? Does this represent a functional regression?

}
}

if (additionalProperties !== undefined && additionalProperties !== false) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the additionalProperties schema is not explicitly specified, its value is treated as false, which differs from the behavior defined by JSON Schema. It might be worth adding a note about this peculiarity.

Adopted the `omitExtraData()` processing algorithm from `svelte-jsonschema-form`, adding in the `removeOptionalEmptyObject()` behavior
- In `@rjsf/utils`:
  - Rewrote `omitExtraData()` basing it on the algorithm from `SJSF`, with improvements to pick up the `removeOptionalEmptyObject()` behavior
  - Refactored the `isValueEmpty()` function from `removeOptionalEmptyObject()` for use by `omitExtraData()` exporting it from utils
  - Deprecated the `removeOptionalEmptyObject()` and `toPathSchema()` functions, `toPathSchema()` on `SchemaUtilsType` and the `PathSchema` type
  - Updated the tests for `toPathSchema()` to add missing coverage after the rewrite
  - Updated the tests for `omitExtraData()` to cover `isValueEmpty()`, the ported `removeOptionalEmptyObject()` behavior and keeping coverage 100%
- In `@rjsf/core`:
  - Updated `Form` to stop using `removeOptionalEmptyObjects()` and deprecating `removeEmptyOptionalObjects` prop.
  - Added tests for `Form` to cover the remove empty objects cases
- Updated the `form-props.md` and `utility-functions.md` to document the deprecations
- Updated the `uiSchema.md` to improve the `enumNames` documents slightly
- Updated the `CHANGELOG.md` accordingly
@heath-freenome heath-freenome force-pushed the adopt-SJSF-omitExtraData-algorithm branch from 42a648e to cb4235a Compare May 18, 2026 06:10
@heath-freenome heath-freenome requested a review from x0k May 18, 2026 06:13
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.

3 participants