Skip to content

[BUG][scala-sttp4] Optional model fields serialize as JSON null instead of being omitted (regression vs scala-sttp) #24360

Description

@vivekmahajan
Description

In the scala-sttp4 generator with jsonLibrary=circe, optional model fields set to None are serialized as JSON null instead of being omitted. This is a regression relative to the scala-sttp (sttp3) generator, whose generated encoders explicitly drop absent optional fields.

Generated Pet.scala (sttp4, circe):

implicit val encoder: Encoder[Pet] = deriveEncoder   // Pet("rex", None) => {"name":"rex","tag":null}

Same schema through the scala-sttp generator:

implicit val encoderPet: Encoder[Pet] = Encoder.instance { t =>
  Json.fromFields{
    Seq(
      Some("name" -> t.name.asJson),
      t.tag.map(v => "tag" -> v.asJson)   // None => field omitted
    ).flatten
  }
}

For OpenAPI, an optional non-nullable property must be absent when unset — many servers (e.g. anything with strict validation, FastAPI/pydantic non-nullable fields with defaults) reject an explicit null with a 422. In practice every request-body PUT/POST from a generated sttp4 client that leaves an optional field at its None default can be rejected, even though the same call from an sttp3-generated client works.

The json4s branch is unaffected (json4s omits None by default), as is scala-sttp4-jsoniter (transientNone defaults to true).

openapi-generator version

7.23.0 (also reproduces on current master).

OpenAPI declaration file content or url
openapi: 3.0.3
info:
  title: Optional field test
  version: 1.0.0
paths:
  /pets:
    post:
      operationId: createPet
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Pet'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
components:
  schemas:
    Pet:
      type: object
      required:
        - name
      properties:
        name:
          type: string
        tag:
          type: string
Generation Details
java -jar openapi-generator-cli.jar generate -g scala-sttp4 -i pet.yaml -o out --additional-properties=jsonLibrary=circe
Steps to reproduce
  1. Generate a client from the spec above.
  2. Pet(name = "rex", tag = None).asJson.noSpaces{"name":"rex","tag":null}.
  3. Generate the same spec with -g scala-sttp → the encoder omits tag when None.
Suggest a fix

PR incoming: append .mapJson(_.dropNullValues) to the generated circe encoders in the scala-sttp4 templates (plain models and oneOf/sealed-trait encoders), restoring sttp3 parity: None ⇒ field omitted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions