Skip to content

Replace Emptyable<T> with SetTracker pattern for explicit null support#3342

Merged
jar-stripe merged 6 commits intomasterfrom
jar/explicit-nulls-v2
Mar 24, 2026
Merged

Replace Emptyable<T> with SetTracker pattern for explicit null support#3342
jar-stripe merged 6 commits intomasterfrom
jar/explicit-nulls-v2

Conversation

@jar-stripe
Copy link
Copy Markdown
Contributor

@jar-stripe jar-stripe commented Mar 24, 2026

Why?

stripe-dotnet needs to distinguish "never set" from "explicitly set to null" on Options properties so that:

  • v1: emptyable fields send field= (empty string) to clear a value
  • v2: nullable fields send "field": null in JSON to clear a value
  • Metadata: v1 sends key= to delete a key; v2 sends "key": null

The previous Emptyable<T> wrapper approach required callers to use a different type. The new SetTracker pattern uses standard nullable properties with backing fields that track whether the setter was called.

What?

  • Breaking: Removed public interfaces IEmptyable and IEmptyable<T>, internal type Emptyable<T>, and their converters
  • BaseOptions.SetTracker now has [JsonIgnore]/[STJS.JsonIgnore] to pass wholesome tests
  • Wholesome test updated to accept STJNullPreservingDictionaryConverter on Dictionary properties
  • Regenerated 263 Options files from sdk-codegen with the new SetTracker pattern:
    • Private backing fields for emptyable properties
    • Setters call this.SetTracker.Track() to record explicit sets
    • Nested options with emptyable fields get IHasSetTracking + SetTracker
    • STJNullPreservingDictionaryConverter on metadata/map properties with nullable elements

See Also

  • DEVSDK-2926

Changelog

  • ⚠️ Full support for unsetting metadata entries and certain Options properties. Set the metadata entry or nullable property to null and the SDK will send an empty string for V1 APIs and a null value for V2 APIs.
    • ⚠️ This changes the meaning of setting a property to null if that property is defined as nullable in our API Ref. If you currently pre-initialize your Options values to null this could have unintended consequences.
  • ⚠️ Removed IEmptyable, IEmptyable<T>, Emptyable<T>, EmptyableConverter<T>, and STJEmptyableConverter<T> — replaced by SetTracker pattern on Options properties.

jar-stripe and others added 3 commits March 23, 2026 08:29
to v2 APIs only when the user explicitly sets a value to null
…ervingDictConverter

- BaseOptions.SetTracker needs [JsonIgnore]/[STJS.JsonIgnore] so the
  PropertiesHaveJsonAttributes wholesome test passes
- SystemTextJsonTestUtils: accept STJNullPreservingDictionaryConverter
  on Dictionary properties (applied by codegen for metadata mutation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Generated by sdk-codegen. Replaces Emptyable<T> wrapper with SetTracker
pattern: private backing fields + property setters that call
SetTracker.Track(). Adds STJNullPreservingDictionaryConverter to
metadata/map properties with nullable elements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
@jar-stripe jar-stripe requested a review from a team as a code owner March 24, 2026 01:19
@jar-stripe jar-stripe requested review from prathmesh-stripe and removed request for a team March 24, 2026 01:19
Breaking: removes public interfaces IEmptyable and IEmptyable<T>.
These are no longer used — all emptyable Options properties now use
the SetTracker pattern with standard nullable types instead.

Also removes internal Emptyable<T> class, EmptyableConverter,
STJEmptyableConverter, and their tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
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