I have a simple case class like so:
case class ColumnMetadata(name: String,
displayName: String,
description: Option[String],
attributeType: AttributeType)
sealed trait AttributeType extends EnumEntry
case object AttributeType extends Enum[AttributeType] with CirceEnum[AttributeType] {
val values: immutable.IndexedSeq[AttributeType] = findValues
case object Number extends AttributeType
case object Text extends AttributeType
case object Percentage extends AttributeType
}
And semi-auto encoder:
package object circe {
implicit val config: Configuration = Configuration.default.withSnakeCaseMemberNames
implicit val columnMetaDecoder: Decoder[ColumnMetadata] = deriveConfiguredDecoder
implicit val columnMetaEncoder: Encoder[ColumnMetadata] = deriveConfiguredEncoder
implicit val columnTypeDecoder: Decoder[AttributeType] = deriveConfiguredDecoder
implicit val columnTypeEncoder: Encoder[AttributeType] = deriveConfiguredEncoder
}
However when I do a serialisation test:
ColumnMetadata("column1", "Column 1", Some("column1"),
AttributeType.withName("Text")).asJson
I get:
{
"name" : "column1",
"display_name" : "Column 1",
"description" : "column1",
"attribute_type" : {
"Text": {}
}
}
When I want:
{
"name" : "column1",
"display_name" : "Column 1",
"description" : "column1",
"attribute_type" : "Text"
}
It works when I use the automatic derivation but I want to use a semi-auto derivation so I can use feature such as withSnakeCaseMemberNames.
This is your error:
implicit val columnTypeDecoder: Decoder[AttributeType] = deriveConfiguredDecoder
implicit val columnTypeEncoder: Encoder[AttributeType] = deriveConfiguredEncoder
This will derive new codecs, treating AttributeType
as any other sealed trait, so it will use discrimination value (semi auto always ignores existing codecs of the type that they are deriving!).
So you are deriving a new AttributeType
codecs, put them in scope where they are used, and so making these new implementations have higher priority than the ones from an companion object. Closer implicit always wins.
If you won't derive the codecs (because there are already existing implementations provided by CirceEnum
trait) then it will work as you expect.
Additionally instead of doing this:
implicit val columnMetaDecoder: Decoder[ColumnMetadata] = deriveConfiguredDecoder
implicit val columnMetaEncoder: Encoder[ColumnMetadata] = deriveConfiguredEncoder
you can just do this:
// make sure that Configuration is in scope by e.g. importing it
// or putting it in package object in the same package as case class
@ConfiguredJsonCodec
case class ColumnMetadata(name: String,
displayName: String,
description: Option[String],
attributeType: AttributeType)
This will spare you effort of creating package of codecs and importing them manually, everywhere you need them. E.g.
// imports
package object models_package {
private[models_package] implicit val config: Configuration = Configuration.default.withSnakeCaseMemberNames
}
package models_package
// imports
@ConfiguredJsonCodec
case class ColumnMetadata(name: String,
displayName: String,
description: Option[String],
attributeType: AttributeType)
sealed trait AttributeType extends EnumEntry
object AttributeType extends Enum[AttributeType] with CirceEnum[AttributeType] {
val values: immutable.IndexedSeq[AttributeType] = findValues
case object Number extends AttributeType
case object Text extends AttributeType
case object Percentage extends AttributeType
}