+
Skip to content

WIP Add support for transforming constructor names. #114

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

Closed
wants to merge 5 commits into from
Closed
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 @@ -18,12 +18,19 @@ sealed trait Configuration {
*/
def transformMemberNames: String => String

/** Transforms the value of any constructor names in the JSON, allowing,
* for example, formatting or case changes
*/
def transformConstructorNames: String => String

def useDefaults: Boolean

def discriminator: Option[String]

protected def getA(transformMemberNames: String => String): Config

protected def getC(transformConstructorNames: String => String): Config

protected def applyDiscriminator(discriminator: Option[String]): Config

/** Creates a configuration which produces snake cased member names */
Expand All @@ -34,6 +41,14 @@ sealed trait Configuration {
final def withKebabCaseMemberNames: Config =
getA(renaming.kebabCase)

/** Creates a configuration which produces snake cased constructor names */
final def withSnakeCaseConstructorNames: Config =
getC(renaming.snakeCase)

/** Creates a configuration which produces kebab cased constructor names */
final def withKebabCaseConstructorNames: Config =
getC(renaming.kebabCase)

final def withDiscriminator(name: String): Config =
applyDiscriminator(Some(name))
}
Expand All @@ -47,19 +62,23 @@ object Configuration {
*/
final case class Codec(
transformMemberNames: String => String,
transformConstructorNames: String => String,
useDefaults: Boolean,
discriminator: Option[String]
) extends Configuration {

type Config = Codec

protected final def getA(transformMemberNames: String => String) =
Codec(transformMemberNames, useDefaults, discriminator)
Codec(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def getC(transformConstructorNames: String => String) =
Codec(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def applyDiscriminator(
discriminator: Option[String]
): Codec =
Codec(transformMemberNames, useDefaults, discriminator)
Codec(transformMemberNames, transformConstructorNames, useDefaults, discriminator)
}

/** Configuration allowing customisation of JSON produced when encoding or
Expand All @@ -69,17 +88,21 @@ object Configuration {
*/
final case class DecodeOnly(
transformMemberNames: String => String,
transformConstructorNames: String => String,
useDefaults: Boolean,
discriminator: Option[String]
) extends Configuration {

type Config = DecodeOnly

protected final def getA(transformMemberNames: String => String) =
DecodeOnly(transformMemberNames, useDefaults, discriminator)
DecodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def getC(transformConstructorNames: String => String) =
DecodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def applyDiscriminator(discriminator: Option[String]) =
DecodeOnly(transformMemberNames, useDefaults, discriminator)
DecodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)
}

/** Configuration allowing customisation of JSON produced when encoding or
Expand All @@ -89,29 +112,33 @@ object Configuration {
*/
final case class EncodeOnly(
transformMemberNames: String => String,
transformConstructorNames: String => String,
useDefaults: Boolean,
discriminator: Option[String]
) extends Configuration {

type Config = EncodeOnly

protected final def getA(transformMemberNames: String => String) =
EncodeOnly(transformMemberNames, useDefaults, discriminator)
EncodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def getC(transformConstructorNames: String => String) =
EncodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

protected final def applyDiscriminator(discriminator: Option[String]) =
EncodeOnly(transformMemberNames, useDefaults, discriminator)
EncodeOnly(transformMemberNames, transformConstructorNames, useDefaults, discriminator)

}

/** Create a default configuration with both decoder and encoder */
val default: Codec =
Codec(identity, true, None)
Codec(identity, identity, true, None)

/** Create a default configuration with **only** encoder */
val encodeOnly: EncodeOnly =
EncodeOnly(identity, true, None)
EncodeOnly(identity, identity, true, None)

/** Create a default configuration with **only** decoder */
val decodeOnly: DecodeOnly =
DecodeOnly(identity, true, None)
DecodeOnly(identity, identity, true, None)
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ private[derivation] final class GenericJsonCodecMacros(val c: blackbox.Context)

private[this] val cfgTransformMemberNames =
q"$config.transformMemberNames"
private[this] val cfgTransformConstructorNames =
q"$config.transformConstructorNames"
private[this] val cfgUseDefaults =
q"$config.useDefaults"
private[this] val cfgDiscriminator =
Expand All @@ -118,11 +120,11 @@ private[derivation] final class GenericJsonCodecMacros(val c: blackbox.Context)
val Type = tpname
(
q"""implicit val $decoderName: $DecoderClass[$Type] =
_root_.io.circe.derivation.deriveDecoder[$Type]($cfgTransformMemberNames, $cfgUseDefaults, $cfgDiscriminator)""",
_root_.io.circe.derivation.deriveDecoder[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgUseDefaults, $cfgDiscriminator)""",
q"""implicit val $encoderName: $AsObjectEncoderClass[$Type] =
_root_.io.circe.derivation.deriveEncoder[$Type]($cfgTransformMemberNames, $cfgDiscriminator)""",
_root_.io.circe.derivation.deriveEncoder[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgDiscriminator)""",
q"""implicit val $codecName: $AsObjectCodecClass[$Type] =
_root_.io.circe.derivation.deriveCodec[$Type]($cfgTransformMemberNames, $cfgUseDefaults, $cfgDiscriminator)"""
_root_.io.circe.derivation.deriveCodec[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgUseDefaults, $cfgDiscriminator)"""
)
} else {
val tparamNames = tparams.map(_.name)
Expand All @@ -138,13 +140,13 @@ private[derivation] final class GenericJsonCodecMacros(val c: blackbox.Context)
val Type = tq"$tpname[..$tparamNames]"
(
q"""implicit def $decoderName[..$tparams](implicit ..$decodeParams): $DecoderClass[$Type] =
_root_.io.circe.derivation.deriveDecoder[$Type]($cfgTransformMemberNames, $cfgUseDefaults, $cfgDiscriminator)""",
_root_.io.circe.derivation.deriveDecoder[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgUseDefaults, $cfgDiscriminator)""",
q"""implicit def $encoderName[..$tparams](implicit ..$encodeParams): $AsObjectEncoderClass[$Type] =
_root_.io.circe.derivation.deriveEncoder[$Type]($cfgTransformMemberNames, $cfgDiscriminator)""",
_root_.io.circe.derivation.deriveEncoder[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgDiscriminator)""",
q"""implicit def $codecName[..$tparams](implicit
..${decodeParams ++ encodeParams}
): $AsObjectCodecClass[$Type] =
_root_.io.circe.derivation.deriveCodec[$Type]($cfgTransformMemberNames, $cfgUseDefaults, $cfgDiscriminator)"""
_root_.io.circe.derivation.deriveCodec[$Type]($cfgTransformMemberNames, $cfgTransformConstructorNames, $cfgUseDefaults, $cfgDiscriminator)"""
)
}
codecType match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ object JsonCodecADTSpecSamples {

@JsonCodec case class ADTTypedA(a: Int) extends ADTTyped
@JsonCodec case class ADTTypedB(b: Int) extends ADTTyped

@JsonCodec(Configuration.default.withKebabCaseConstructorNames)
sealed trait ADTTransformed

@JsonCodec case class ADTTransformed1(a: Int) extends ADTTransformed
@JsonCodec case class ADTTransformed2(b: Int) extends ADTTransformed

@JsonCodec(Configuration.default.withSnakeCaseConstructorNames.withDiscriminator("_type"))
sealed trait ADTSnakeDiscriminator

@JsonCodec case class ADTSnakeDiscriminatorA(a: Int) extends ADTSnakeDiscriminator
@JsonCodec case class ADTSnakeDiscriminatorB(b: Int) extends ADTSnakeDiscriminator
}

class JsonCodecADTSpec extends WordSpec with Matchers {
Expand Down Expand Up @@ -77,5 +89,29 @@ class JsonCodecADTSpec extends WordSpec with Matchers {
Right(b1)
)
}

"transform constructor names" in {
val a1: ADTTransformed = ADTTransformed1(1)

a1.asJson.pretty(printer) should be("""{"adt-transformed1":{"a":1}}""")
parse("""{"adt-transformed1":{"a":1}}""").right.get.as[ADTTransformed] should be(Right(a1))

val b1: ADTTransformed = ADTTransformed2(1)

b1.asJson.pretty(printer) should be("""{"adt-transformed2":{"b":1}}""")
parse("""{"adt-transformed2":{"b":1}}""").right.get.as[ADTTransformed] should be(Right(b1))
}

"transform constructor names with a discriminator" in {
val a1: ADTSnakeDiscriminator = ADTSnakeDiscriminatorA(1)

a1.asJson.pretty(printer) should be("""{"a":1,"_type":"adt_snake_discriminator_a"}""")
parse("""{"a":1,"_type":"adt_snake_discriminator_a"}""").right.get.as[ADTSnakeDiscriminator] should be(Right(a1))

val b1: ADTSnakeDiscriminator = ADTSnakeDiscriminatorB(1)

b1.asJson.pretty(printer) should be("""{"b":1,"_type":"adt_snake_discriminator_b"}""")
parse("""{"b":1,"_type":"adt_snake_discriminator_b"}""").right.get.as[ADTSnakeDiscriminator] should be(Right(b1))
}
}
}
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载