-
Notifications
You must be signed in to change notification settings - Fork 424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
codegen: Jsoniter support (#840) #3610
codegen: Jsoniter support (#840) #3610
Conversation
openapi-codegen/core/src/main/scala/sttp/tapir/codegen/ClassDefinitionGenerator.scala
Outdated
Show resolved
Hide resolved
jsonParamRefs.toSeq.flatMap(ref => allSchemas.get(ref.stripPrefix("#/components/schemas/"))) | ||
) | ||
|
||
val maybeJsonSerdeHelpers = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Shouldn't jsoniter-scala derive these automatically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would expect that, wouldn't you? I haven't really used jsoniter-scala much before so perhaps there's a less ham-fisted way to do this, but without these I get a lot of could not find implicit value for evidence parameter of type com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[List[sttp.tapir.generated.TapirGeneratedEndpoints.MyCaseClass]]
and whatnot. My understanding is that it's expected that all top-level json formats will require an explicit serde declaration (and, honestly, I'm just relieved that the implicit defs work for the Option/List wrappers, so that we don't need to declare every single one of them individually)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an ability to auto-derive codecs using an in-lined compile-time configuration, like here or here. But I'm not sure that it can be reusable here. Another approach taken by smithy4s is just defining all codecs for basic types and deriving for collections and complex ones like sum and product types: https://github.com/disneystreaming/smithy4s/blob/a5216184a5e584795c56a7a5a4def917849943f1/modules/json/src/smithy4s/json/internals/SchemaVisitorJCodec.scala
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm it works in a simple isolated experiment:
//>using dep "com.github.plokhotnyuk.jsoniter-scala:jsoniter-scala-core_3:2.28.4"
//>using dep "com.github.plokhotnyuk.jsoniter-scala:jsoniter-scala-macros_3:2.28.4"
import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._
case class MySimpletype2(v: String)
case class MySimpleType(a: Int, b: List[MySimpletype2])
object Main extends App {
implicit val codec: JsonValueCodec[MySimpleType] = com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make[MySimpleType]
val s = writeToString[MySimpleType](MySimpleType(1, List(MySimpletype2("a"))))
println(s)
}
maybe it has something to do with location of the classes or the main CC codec 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe it works only for Scala 3 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That example should work, but I think if you tried val s = writeToString[List[MySimpleType]](List(MySimpleType(1, List(MySimpletype2("a")))))
it would break. One thing I have only just appreciated is that jsoniter-scala doesn't need the intermediary serdes, so we needn't generate them for every schema model, only the top-level ones. Anyway, that's a different issue than the serdes defined here for primitive types, lists and options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I understand now. Thanks for clarifying the difference @hughsimpson. The approach from Smithy4s looks interesting, thanks a lot for chiming in @plokhotnyuk.
I think I am OK with current solution, looks like a good start and it could be optimized in the future.
Starts to address #840
Adds support for jsoniter serdes in codegen.
Since, in order to support jsoniter, it became necessary to explicitly define the desired serdes, I've also moved from
io.circe.generic.auto._
toio.circe.generic.semiauto._
; the latter of which has the notable benefit that if it's going to fail to compile, it doesn't take until the heat death of the universe to do so.I've also modified the model generation. When multipart file support was initially added, the generated type for
format: binary
strings was changed tosttp.model.Part[java.io.File]
, which notably does not work with json serialisation. In order to retain that support, I've added a check so that if a schema is not used in any application/json content we'll retain the File model, and otherwise use Array[Byte]. This means that a model defining a binary string type cannot be reused betweenmultipart/form-data
andapplication/json
contexts, but I honestly have no idea how to avoid that limitation.Extra edit: it turns out supporting adt serialisation with jsoniter-scala requires that the adt serdes be in a separate
modulefile to the class declarations, so I'm immediately regretting this when trying to implement oneOf support but hey ho