-
Notifications
You must be signed in to change notification settings - Fork 25
Change default optional null handling to accept null values #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Implements .orNull() modifier with two styles: - .type: Uses type array ["integer", "null"] for scalar primitives - .union: Uses oneOf composition for complex types Features: - Per-property opt-in via @SchemaOptions(.orNull(style:)) - Global opt-in via @Schemable(optionalNulls: true) - Automatic style selection (scalar primitives use .type, complex types use .union) By default, optional properties do NOT accept null values (only omission), maintaining minimal schemas. Users must explicitly opt in to null acceptance.
ajevans99
requested changes
Nov 4, 2025
Sources/JSONSchemaBuilder/Documentation.docc/Articles/Macros.md
Outdated
Show resolved
Hide resolved
Tests/JSONSchemaIntegrationTests/OptionalNullsIntegrationTests.swift
Outdated
Show resolved
Hide resolved
Resolved merge conflicts by accepting both changes: - Kept optionalNulls parameter from feature branch - Integrated context parameter and diagnostic validation from main - Updated all SchemaGenerator initializers to accept both parameters - Updated SchemableMember.generateSchema to handle both features
BREAKING CHANGE: Optional properties now accept null by default Previously, optional properties (Int?, String?, etc.) would reject explicit null values and only accept field omission. This changes the default behavior to accept both null and omission, which better aligns with common JSON API practices. Changes: - Set optionalNulls parameter default to true in @Schemable macro - Updated all tests to reflect new default behavior - Updated documentation with new defaults and opt-out examples - Regenerated snapshot tests Migration: Users who need the old strict behavior can opt-out: @Schemable(optionalNulls: false) Related to ajevans99#124
Update test expectations to reflect that optional properties now get .orNull() and .flatMapOptional() by default, matching the new behavior where optionalNulls defaults to true.
ajevans99
approved these changes
Nov 5, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
Changes the default behavior for optional properties in the
@Schemablemacro to accept both missing fields and explicitnullvalues. Previously, optional properties (Int?,String?, etc.) would accept missing fields but reject explicitnullvalues, which didn't align with common JSON API practices wherenullis used interchangeably with field omission.This PR implements the
.orNull()modifier with two implementation styles and makes it the default behavior:.type: Uses type array notation["integer", "null"]for scalar primitives (clearer validation errors).union: Uses oneOf composition for complex types (objects, arrays, refs)The macro now automatically applies
.orNull()to all optional properties by default, with opt-out available for stricter validation.Usage
Default behavior (allows null):
Opt-out (forbid null):
Per-property override:
When
optionalNulls: true(the default), the macro automatically selects the appropriate style:.type.unionType of Change
.orNull()modifier)Implementation Details
Files Changed:
Sources/JSONSchemaBuilder/JSONComponent/Modifier/OrNullModifier.swift(new)OrNullTypeComponentfor type array styleOrNullUnionComponentfor oneOf composition style.orNull(style:)method onJSONSchemaComponentSources/JSONSchemaBuilder/Macros/SchemaOptions/SchemaOptions.swift.orNull(style:)option to@SchemaOptionsmacroSources/JSONSchemaBuilder/Macros/Schemable.swiftoptionalNulls: Bool = trueparameter to@Schemablemacro (default: true)Sources/JSONSchemaMacro/Schemable/SchemableMacro.swiftoptionalNullsparameter to schema generatorstruewhen parameter is omittedSources/JSONSchemaMacro/Schemable/SchemaGenerator.swiftoptionalNullsflag through schema generationtrueSources/JSONSchemaMacro/Schemable/SchemableMember.swift.orNull()annotations in@SchemaOptions.orNull()when global flag istrue(default behavior).typeor.unionstyle based on type complexitySources/JSONSchemaMacro/Schemable/SupportedPrimitive.swiftisScalarproperty to distinguish scalar primitives from complex typesSources/JSONSchemaBuilder/Documentation.docc/Articles/Macros.mdoptionalNulls: falseTesting
Comprehensive test coverage including:
optionalNulls: false@SchemaOptions(.orNull(style:))All existing tests updated to reflect new defaults. Snapshot tests regenerated.
Design Decisions
Opt-out by default (breaking change):
nulland omission as equivalent for optional fieldsoptionalNulls: falsefor strict validation needsTwo-style approach:
.typearray for primitives (better error messages).unioncomposition for complex types (required by JSON Schema spec)Boolean parameter over enum:
optionalNulls: true/falseis more idiomatic than enum casesNo cascading:
Breaking Changes
Before: Optional properties rejected explicit
nullvalues by defaultAfter: Optional properties accept both
nulland omission by defaultMigration: Users who need the old strict behavior can opt-out:
Rationale for Breaking Change
After discussion in #124, the consensus is that:
nulland omission interchangeably for optional valuesThe breaking change is justified by significantly improved ergonomics and alignment with ecosystem norms.
Additional Notes
Related to GitHub issue #124 about optional properties and null handling.
The
optionalNullsflag is not cascading - it only applies to the properties of the type where it's declared. To enable/disable null acceptance for nested types, apply the appropriate@Schemable(optionalNulls:)parameter to each type individually.