Openapi-specification: Question: nullable and enum

Created on 21 Apr 2019  路  14Comments  路  Source: OAI/OpenAPI-Specification

Hey,

I am hoping to get clarification on the following:

When the nullable and enum keywords are used together, which takes precedence?

i.e.

type: string
nullable: true
enum:
  - one
  - two
propertyA = null

When propertyA is validated against the definition. Should the result be:

  1. true, since the it's nullable
  2. false, because null is not part of the enum
  3. something else?

All 14 comments

@left-rite, your first answer is correct:

When propertyA is validated against the definition. Should the result be:

  1. true, since it's nullable.

I think of nullable is a shortcut for an anyOf applicator with two subschemas, like this:

anyOf:
- type: "null"
- {schemaObject}

... where {schemaObject} is a placeholder for the schema you've defined, excluding the nullable keyword.

(Please note that type: "null" is supported in JSON Schema, but not in OpenAPI. OpenAPI uses the nullable keyword for this purpose.)

Using this theoretical translation, your example would translate into this JSON Schema:

anyOf:
- type: "null"
- type: string
  enum:
  - one
  - two  

This is consistent with the way nullable works in other cases. enum is a validation constraint, just like type, pattern, minItems, etc. In all cases, _non-null_ values must validate against these constraints. But if nullable is true, then null is allowed as a value regardless of any other constraints.

The docs say here that "enum" behaves as defined in JSON Schema spec. "enum" being affected by "nullable" is not consistent with JSON Schema spec.

@epoberezkin , I would say that _all_ assertions in JSON Schema are affected by nullable. If the Schema Object is nullable, the value can non-null and conformant to the schema; or it can be null, in which case _none_ of the schema constraints apply.

@epoberezkin , looking more closely at the JSON Schema spec:

3.2.1. Assertions and Instance Primitive Types

Most validation assertions only constrain values within a certain primitive type. When the type of the instance is not of the type targeted by the keyword, the instance is considered to conform to the assertion.

For example, the "maxLength" keyword will only restrict certain strings (that are too long) from being valid. If the instance is a number, boolean, null, array, or object, then it is valid against this assertion.

Does enum apply to null values?

6.1.2. enum

The value of this keyword MUST be an array. This array SHOULD have at least one element. Elements in the array SHOULD be unique.

An instance validates successfully against this keyword if its value is equal to one of the elements in this keyword's array value.

_Elements in the array might be of any value, including null._

JSON Schema categorizes its keywords as _assertions_, _annotations_, and _applicators_. A few keywords may function as more than one of these categories.

I think nullable is problematic because it doesn't fit into any of these categories. Where assertions constrain the allowed value space, nullable has the effect of _expanding_ the value space to include null, which would otherwise be disallowed.

You can easily see how this could cause a problem with allOf inheritance, where adding nullable to a subtype violates the constraints of the supertype:

```.yaml
Bird:
type: object
properties:
order:
type: string
genus:
type: string
species:
type: string
flightSpeed:
type: number

FlightlessBird:
  allOf:
  - $ref: "#/components/schemas/Bird"
  properties:
    flightSpeed:
      type: number
      nullable: true

```

But JSON Schema allows all kinds of contradictory schemas. So using it in a subtype would not override the constraints of the supertype. An object with flightSpeed: null would still fail validation against FlightlessBird, because it's prohibited in the supertype.

I'm inclined to agree that the OpenAPI spec should be clarified here, but we would need to figure out how best to do it.

OpenAPI spec should be clarified here, but we would need to figure out how best to do it.

2 options:

  1. exclude "enum" from the list of keywords that follow JSON Schema spec and to re-define "enum" to allow null value with "nullable" even if null is not explicitly listed in "enum".
  2. require that "enum" explicitly lists null to allow it.

@epoberezkin , I like your suggested options.

They also seem to correspond to two different translations of nullable:


1. nullable: true --> anyOf: [s, type: 'null']

Where _s_ is the full set of assertions (excluding nullable) defined in the schema. This is the interpretation I described here, where schema assertions apply only to non-null values.

So enum, like other assertions, wouldn't apply here. This has the same effect as redefining enum in OpenAPI to implicitly allow null values when nullable is true.


2. nullable: true --> type: [t, 'null']

Where _t_ is the type specified in the schema.

Or, if type is not specified in the schema, _t_ is the list of all allowed, non-null types. In that case, nullable: true translates to type: [integer, number, string, boolean, object, array, null]

This is another plausible translation of nullable to JSON Schema. Semantically, it's different from the first one because all of the other schema assertions, including enum, would still apply. In that case, an enum would disallow null values unless it includes null explicitly as one of its enumerated values.


I would rather see the spec clarification done in this form, as a translation of nullable: true to one of these two JSON Schema constructs. Though it's not as straightforward as what you proposed, it accomplishes a few useful things:

  1. Reconciles OpenAPI with JSON Schema, providing an unambiguous translation so we can apply standard JSON Schema validation semantics.
  2. Allows for automated translation so we can make use of actual implementations of JSON Schema validation.
  3. Gets us out of the danger zone wherein nullable is an entirely new category of keyword that expands the value space, rather than constraining or annotating.
  4. Clarifies the interactions between nullable and other schema assertions, aside from enum, that are also type-agnostic. Right now it looks like const is the only other type-agnostic assertion, and it's not supported in OpenAPI. But later revisions of the spec could add support for const, or other type-agnostic assertions introduced either directly by OpenAPI or via JSON Schema.

Note the related discussion in #1389, which proposes replacing nullable with a 2-element type array, where one element is the name of a supported type and the other element is "null". This is a special case of JSON Schema's type arrays, so it aligns better with JSON schema. And it matches translation 2, in my comment above.

So I started out saying that translation 1 was the correct way to interpret nullable, but it seems like there's more of a consensus around translation 2.

And in that case, the enum constraint would still apply, and would prohibit null values unless the enum explicitly includes null.

Please note that a proposed clarification is now under review, hopefully to be included in a v3.0.3 patch release of the OpenAPI spec:

Proposal: Clarify Semantics of nullable in OpenAPI 3.0

@tedepstein @MikeRalphson @webron since the clarification did make it into v3.0.3 can this be closed now?

Yup!

I see this issue is closed, but maybe I can still get clarification on the recommended approach to define in OpenAPI 3.0.x a data type where the set of allowed values consist on an enum (e.g. of type string) and also the null value. Since nullable: true seems to be not correct anymore, how should it be defined?

Would this approach be ok, for example?

NullableEnum:
  anyOf:
    - type: string
      enum:
        - one
        - two
    - enum: [ null ]

@jdegre, that would work. But this also works:

NullableEnum:
  type: string
  nullable: true
  enum:
    - one
    - two
    - null

The combination of type: string with nullable: true is equivalent to the type array type: [string, "null"] in JSON Schema. And in fact, the current plan is to support full JSON Schema in OpenAPI 3.1. So in OpenAPI 3.1, this will be allowed:

NullableEnum:
  type: [string, "null"]
  enum:
    - one
    - two
    - null

thanks a lot for the fast reply @tedepstein!

For posterity, we had an issue using the python generator with this format:

NestedNullableEnum:
  type: string
  nullable: true
  enum:
    - one
    - two
    - null
Could not generate model 'NestedNullableEnum'
    at org.openapitools.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:542)
    at org.openapitools.codegen.DefaultGenerator.generate(DefaultGenerator.java:879)
    at org.openapitools.codegen.cmd.Generate.execute(Generate.java:432)
    at org.openapitools.codegen.cmd.OpenApiGeneratorCommand.run(OpenApiGeneratorCommand.java:32)
    at org.openapitools.codegen.OpenAPIGenerator.main(OpenAPIGenerator.java:66)
Caused by: java.lang.NullPointerException: Null context for variable 'isString' on line 141

But this one worked fine:

NestedNullableEnum:
  anyOf:
    - type: string
      enum:
        - one
        - two
    - enum: [ null ]
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jblazek picture jblazek  路  3Comments

ricellis picture ricellis  路  3Comments

john1452 picture john1452  路  5Comments

kolisko picture kolisko  路  4Comments

muhmud picture muhmud  路  5Comments