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
- Generate a client from the spec above.
Pet(name = "rex", tag = None).asJson.noSpaces → {"name":"rex","tag":null}.
- 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.
Description
In the
scala-sttp4generator withjsonLibrary=circe, optional model fields set toNoneare serialized as JSONnullinstead of being omitted. This is a regression relative to thescala-sttp(sttp3) generator, whose generated encoders explicitly drop absent optional fields.Generated
Pet.scala(sttp4, circe):Same schema through the
scala-sttpgenerator: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
nullwith a 422. In practice every request-body PUT/POST from a generated sttp4 client that leaves an optional field at itsNonedefault can be rejected, even though the same call from an sttp3-generated client works.The json4s branch is unaffected (json4s omits
Noneby default), as isscala-sttp4-jsoniter(transientNonedefaults to true).openapi-generator version
7.23.0 (also reproduces on current
master).OpenAPI declaration file content or url
Generation Details
Steps to reproduce
Pet(name = "rex", tag = None).asJson.noSpaces→{"name":"rex","tag":null}.-g scala-sttp→ the encoder omitstagwhenNone.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.