Skip to content
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

Update upickle to 3.3.1 #3773

Merged
merged 2 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ class CreateDerivedEnumerationPickler[T: ClassTag](

override lazy val writer: Writer[T] =
new TaggedWriter.Node[T](childReadWriters.map(_._2.asInstanceOf[TaggedWriter[T]]): _*) {
override def findWriter(v: Any): (String, ObjectWriter[T]) =
val (t, writer) = super.findWriter(v)
override def findWriterWithKey(v: Any): (String, String, ObjectWriter[T]) =
val (tagKey, tagValue, writer) = super.findWriterWithKey(v)
// Here our custom encoding transforms the value of a singleton object
val overriddenTag = encode(v.asInstanceOf[T]).toString
(overriddenTag, writer)
(tagKey, overriddenTag, writer)
}
}
new Pickler[T](tapirPickle, schema)
Expand All @@ -77,10 +77,14 @@ class CreateDerivedEnumerationPickler[T: ClassTag](
// https://github.com/softwaremill/tapir/issues/3192
override lazy val writer = annotate[C](
SingletonWriter[C](null.asInstanceOf[C]),
Annotator.defaultTagKey, // not used in enumerations
upickleMacros.tagName[C],
Annotator.Checker.Val(upickleMacros.getSingleton[C])
)
override lazy val reader = annotate[C](SingletonReader[C](upickleMacros.getSingleton[C]), upickleMacros.tagName[C])
override lazy val reader = annotate[C](SingletonReader[C](upickleMacros.getSingleton[C]),
Annotator.defaultTagKey, // not used in enumerations
upickleMacros.tagName[C]
)
}
(pickle.reader, pickle.writer)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import scala.quoted.*
import scala.reflect.ClassTag
import scala.util.{Failure, NotGiven, Success, Try}
import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger}
import macros.*

import scala.annotation.implicitNotFound
import sttp.tapir.json.pickler.SubtypeDiscriminator
import sttp.tapir.generic.Configuration

object Pickler:
Expand Down Expand Up @@ -56,7 +54,7 @@ object Pickler:
type ParamV = V
val subtypeDiscriminator: SubtypeDiscriminator[T] = new CustomSubtypeDiscriminator[T] {
type V = ParamV
override lazy val fieldName = c.discriminator.getOrElse(SubtypeDiscriminator.DefaultFieldName)
override lazy val fieldName = c.discriminator
override def extractor = extractorFn
override def asString = asStringFn
override lazy val mapping = paramMapping
Expand Down Expand Up @@ -353,8 +351,6 @@ object Pickler:
val childDefaults = enrichedChildSchemas.map(_.default.map(_._1))

val tapirPickle = new TapirPickle[T] {
override def tagName = config.discriminator.getOrElse(super.tagName)

override lazy val writer: Writer[T] =
macroProductW[T](
schema,
Expand All @@ -367,7 +363,8 @@ object Pickler:
schema,
childPicklers.map([a] => (obj: a) => obj.asInstanceOf[Pickler[a]].innerUpickle.reader),
childDefaults,
product
product,
config
)
}
Pickler[T](tapirPickle, schema)
Expand All @@ -387,7 +384,6 @@ object Pickler:
): Pickler[T] =
val childPicklersList = childPicklers.productIterator.toList.asInstanceOf[List[Pickler[_ <: T]]]
val tapirPickle = new TapirPickle[T] {
override def tagName = subtypeDiscriminator.fieldName
override lazy val writer: Writer[T] =
macroSumW[T](
childPicklersList,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package sttp.tapir.json.pickler

import sttp.tapir.generic.Configuration
import upickle.core.Annotator

final case class PicklerConfiguration(genericDerivationConfig: Configuration) {
export genericDerivationConfig.{toEncodedName, discriminator, toDiscriminatorValue}
export genericDerivationConfig.{toEncodedName, toDiscriminatorValue}

def discriminator: String = genericDerivationConfig.discriminator.getOrElse(Annotator.defaultTagKey)

def withSnakeCaseMemberNames: PicklerConfiguration = PicklerConfiguration(genericDerivationConfig.withSnakeCaseMemberNames)
def withScreamingSnakeCaseMemberNames: PicklerConfiguration = PicklerConfiguration(
Expand Down Expand Up @@ -32,6 +35,6 @@ final case class PicklerConfiguration(genericDerivationConfig: Configuration) {

object PicklerConfiguration {
given default: PicklerConfiguration = PicklerConfiguration(
Configuration.default.copy(discriminator = Some(SubtypeDiscriminator.DefaultFieldName))
Configuration.default.copy(discriminator = Some(Annotator.defaultTagKey))
)
}
25 changes: 17 additions & 8 deletions json/pickler/src/main/scala/sttp/tapir/json/pickler/Readers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,17 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
override def findReader(s: String) = if (s == leafTagValue) r else null
}

override def annotate[V](rw: Reader[V], n: String) = {
LeafWrapper(new TaggedReader.Leaf[V](n, rw), rw, n)
override def annotate[V](rw: Reader[V], key: String, value: String) = {
LeafWrapper(new TaggedReader.Leaf[V](key, value, rw), rw, value)
}

inline def macroProductR[T](schema: Schema[T], childReaders: Tuple, childDefaults: List[Option[Any]], m: Mirror.ProductOf[T]): Reader[T] =
inline def macroProductR[T](
schema: Schema[T],
childReaders: Tuple,
childDefaults: List[Option[Any]],
m: Mirror.ProductOf[T],
config: PicklerConfiguration
): Reader[T] =
val schemaFields = schema.schemaType.asInstanceOf[SchemaType.SProduct[T]].fields

val reader = new CaseClassReadereader[T](upickleMacros.paramsCount[T], upickleMacros.checkErrorMissingKeysCount[T]()) {
Expand All @@ -44,8 +50,9 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
}
}

inline if upickleMacros.isSingleton[T] then annotate[T](SingletonReader[T](upickleMacros.getSingleton[T]), upickleMacros.tagName[T])
else if upickleMacros.isMemberOfSealedHierarchy[T] then annotate[T](reader, upickleMacros.tagName[T])
inline if upickleMacros.isSingleton[T] then
annotate[T](SingletonReader[T](upickleMacros.getSingleton[T]), config.discriminator, upickleMacros.tagName[T])
else if upickleMacros.isMemberOfSealedHierarchy[T] then annotate[T](reader, config.discriminator, upickleMacros.tagName[T])
else reader

inline def macroSumR[T](childPicklers: List[Pickler[_]], subtypeDiscriminator: SubtypeDiscriminator[T]): Reader[T] =
Expand All @@ -60,25 +67,27 @@ private[pickler] trait Readers extends ReadersVersionSpecific with UpickleHelper
.map { case (k, v) => (k, v.innerUpickle.reader) }
.map {
case (k, leaf) if leaf.isInstanceOf[LeafWrapper[_]] =>
TaggedReader.Leaf[T](discriminator.asString(k), leaf.asInstanceOf[LeafWrapper[_]].r.asInstanceOf[Reader[T]])
TaggedReader
.Leaf[T](discriminator.fieldName, discriminator.asString(k), leaf.asInstanceOf[LeafWrapper[_]].r.asInstanceOf[Reader[T]])
case (_, otherKindOfReader) =>
otherKindOfReader
}

new TaggedReader.Node[T](readersFromMapping.asInstanceOf[Seq[TaggedReader[T]]]: _*)
new TaggedReader.Node[T](discriminator.fieldName, readersFromMapping.asInstanceOf[Seq[TaggedReader[T]]]: _*)

case discriminator: DefaultSubtypeDiscriminator[T] =>
val readers = childPicklers.map(cp => {
(cp.schema.name, cp.innerUpickle.reader) match {
case (Some(sName), wrappedReader: Readers#LeafWrapper[_]) =>
TaggedReader.Leaf[T](
discriminator.fieldName,
discriminator.toValue(sName),
wrappedReader.r.asInstanceOf[Reader[T]]
)
case _ =>
cp.innerUpickle.reader.asInstanceOf[Reader[T]]
}
})
Reader.merge(readers: _*)
Reader.merge(subtypeDiscriminator.fieldName, readers: _*)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import sttp.tapir.Schema.SName
private[pickler] sealed trait SubtypeDiscriminator[T]:
def fieldName: String

object SubtypeDiscriminator:
val DefaultFieldName = "$type"

/** Describes non-standard encoding/decoding for subtypes in sealed hierarchies. Allows specifying an extractor function, for example to
* read subtype discriminator from a field. Requires also mapping in the opposite direction, to specify how to read particular
* discriminator values into concrete subtype picklers.
Expand All @@ -27,4 +24,4 @@ private[pickler] case class DefaultSubtypeDiscriminator[T](fieldName: String, to

private[pickler] object DefaultSubtypeDiscriminator:
def apply[T](config: PicklerConfiguration): DefaultSubtypeDiscriminator[T] =
new DefaultSubtypeDiscriminator[T](config.discriminator.getOrElse(SubtypeDiscriminator.DefaultFieldName), config.toDiscriminatorValue)
new DefaultSubtypeDiscriminator[T](config.discriminator, config.toDiscriminatorValue)
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,14 @@ private[pickler] trait Writers extends WritersVersionSpecific with UpickleHelper
inline if upickleMacros.isMemberOfSealedHierarchy[T] && !isEnumeration[T] then
annotate[T](
writer,
config.discriminator,
schema.name.map(config.toDiscriminatorValue).getOrElse(upickleMacros.tagName[T]),
Annotator.Checker.Cls(implicitly[ClassTag[T]].runtimeClass)
) // tagName is responsible for extracting the @tag annotation meaning the discriminator value
else if upickleMacros.isSingleton[T]
then // moved after "if MemberOfSealed" to handle case objects in hierarchy as case classes - with discriminator, for consistency
// here we handle enums
annotate[T](SingletonWriter[T](null.asInstanceOf[T]), upickleMacros.tagName[T], Annotator.Checker.Val(upickleMacros.getSingleton[T]))
annotate[T](SingletonWriter[T](null.asInstanceOf[T]), config.discriminator, upickleMacros.tagName[T], Annotator.Checker.Val(upickleMacros.getSingleton[T]))
else writer

inline def macroSumW[T: ClassTag](childPicklers: => List[Pickler[? <: T]], subtypeDiscriminator: SubtypeDiscriminator[T])(using
Expand All @@ -84,14 +85,14 @@ private[pickler] trait Writers extends WritersVersionSpecific with UpickleHelper
val writers: List[TaggedWriter[_ <: T]] = childPicklers.map(_.innerUpickle.writer.asInstanceOf[TaggedWriter[_ <: T]])

new TaggedWriter.Node[T](writers: _*) {
override def findWriter(v: Any): (String, ObjectWriter[T]) = {
override def findWriterWithKey(v: Any): (String, String, ObjectWriter[T]) = {
subtypeDiscriminator match {
case discriminator: CustomSubtypeDiscriminator[T] =>
val (tag, w) = super.findWriter(v)
val (tagKey, tagValue, w) = super.findWriterWithKey(v)
val overriddenTag = discriminator.writeUnsafe(v) // here we use our discirminator instead of uPickle's
(overriddenTag, w)
(tagKey, overriddenTag, w)
case _ =>
super.findWriter(v)
super.findWriterWithKey(v)
}
}
}
2 changes: 1 addition & 1 deletion project/Versions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Versions {
val pekkoHttp = "1.0.1"
val pekkoStreams = "1.0.2"
val swaggerUi = "5.17.2"
val upickle = "3.3.0"
val upickle = "3.3.1"
val playJson = "3.0.1"
val play29Json = "3.0.3"
val finatra = "24.2.0"
Expand Down
Loading