Skip to content

Support type: null in anyOf/oneOf/allOf as a nullability marker - #933

Closed
ThomasDutartre wants to merge 1 commit into
apple:mainfrom
ThomasDutartre:feat/support-nullables
Closed

ThomasDutartre wants to merge 1 commit into
apple:mainfrom
ThomasDutartre:feat/support-nullables

Conversation

@ThomasDutartre

@ThomasDutartre ThomasDutartre commented Aug 6, 2026 •

Copy link
Copy Markdown

Motivation

In OpenAPI 3.1, a nullable value is commonly expressed by adding a { "type": "null" } branch to an anyOf/oneOf/allOf (e.g. anyOf: [$ref, {type: null}]). Today the generator treats the null branch as an unsupported member, so the whole composition is skipped and emits Schema "null" is not supported ... skipping. This blocks common specs (FastAPI, Figma, and others) and is tracked in #906, #817, #419, #565, #513, #286.

OpenAPIKit already propagates the nullability onto the enclosing composition's core context when one of its subschemas is null, so the generator only needs to stop rejecting these schemas and skip the null member. This is the minimal, non-collapsing approach previously suggested by the maintainers, which keeps API evolution deterministic (adding a member expands the wrapper instead of changing its shape).

Prior maintainer guidance (the "filter" approach)

This PR intentionally implements the filtering approach that @czechboy0 described earlier, rather than the collapsing/optional-root approach of #557 and #558:

Minimal example that should be supported

{
  "components": {
    "schemas": {
      "Child": { "type": "string" },
      "Parent": {
        "type": "object",
        "properties": {
          "child": {
            "anyOf": [
              { "$ref": "#/components/schemas/Child" },
              { "type": "null" }
            ]
          }
        }
      }
    }
  }
}

Modifications

  • Added JSONSchema.isNullType helper.
  • isSchemaSupported: filter out the null branch of allOf/anyOf/oneOf before checking support, so the composition is generated instead of skipped.
  • translateAllAnyOneOf: skip the null branch when generating members/cases (no member/case is emitted for it).
  • translateArray: preserve the element's optionality ([Element?]), consistent with how builtin element types are already emitted, so arrays that mix values and null are supported.

Result

  • anyOf/oneOf/allOf containing a type: null branch are now generated as an optional wrapper (e.g. var b: A.bPayload?) instead of being skipped.
  • The wrapper is not collapsed: oneOf: [$ref, null] yields a single-case optional enum, and adding a case simply expands it — API evolution stays deterministic.
  • Arrays of nullable elements are emitted as [Element?], preserving order while allowing null entries.
  • Non-nullable compositions and arrays are unchanged.

Test Plan

  • Added snippet tests in SnippetBasedReferenceTests for oneOf/anyOf with null (single and multiple refs) and for an array with a nullable oneOf element.
  • swift test: 329 tests, 0 failures.
  • Validated end-to-end against a real-world spec: generation succeeds with no Schema "null" is not supported warnings, and nullable properties/array elements are emitted as optionals.

Treat a `type: null` branch inside anyOf/oneOf/allOf purely as a
nullability marker (already reflected on the composition's core context
by OpenAPIKit) instead of an unsupported member. Such compositions are
now generated as an optional wrapper without collapsing, keeping API
evolution deterministic.

Also preserve array element optionality so nullable elements are emitted
as `[Element?]`, consistent with builtin element types, allowing arrays
that mix values and nulls while preserving order.

Adds snippet tests for oneOf/anyOf with null (single and multiple refs)
and for arrays with a nullable oneOf element.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ThomasDutartre
ThomasDutartre marked this pull request as ready for review August 6, 2026 15:29
@saikatdas0790

Copy link
Copy Markdown

Any movement on this? Would appreciate this getting merged in

@czechboy0

Copy link
Copy Markdown
Contributor

Hi @ThomasDutartre,

we're formalizing how we organize issues and PRs, and we now expect an issue to be assigned to a contributor before a PR is opened. Details in https://github.com/apple/swift-openapi-generator/blob/main/CONTRIBUTING.md#how-to-contribute-your-work. So I'll close this PR for now.

If you're still interested in working on this issue, let's discuss next steps in #906.

Thanks! 🙏

@czechboy0 czechboy0 closed this Sep 7, 2026
@ThomasDutartre

Copy link
Copy Markdown
Author

Hi @ThomasDutartre,

we're formalizing how we organize issues and PRs, and we now expect an issue to be assigned to a contributor before a PR is opened. Details in https://github.com/apple/swift-openapi-generator/blob/main/CONTRIBUTING.md#how-to-contribute-your-work. So I'll close this PR for now.

If you're still interested in working on this issue, let's discuss next steps in #906.

Thanks! 🙏

I literally quoted all the linked issues, even your comment that suggested this approach

@czechboy0

Copy link
Copy Markdown
Contributor

It's not about quoting, it's about assigning an issue to a specific person before a PR is opened. Otherwise we get a lot of duplicated PRs and it's quite time-consuming on the maintainers to resolve and ensure everyone gets the credit for the work they did.

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