Сериализация Enumeratum Circe
У меня есть простой класс case:
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
}
И полуавтоматический кодировщик:
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
}
Однако, когда я провожу тест сериализации:
ColumnMetadata("column1", "Column 1", Some("column1"),
AttributeType.withName("Text")).asJson
Я получил:
{
"name" : "column1",
"display_name" : "Column 1",
"description" : "column1",
"attribute_type" : {
"Text": {}
}
}
Когда я хочу:
{
"name" : "column1",
"display_name" : "Column 1",
"description" : "column1",
"attribute_type" : "Text"
}
Это работает, когда я использую автоматический вывод, но я хочу использовать полуавтоматический вывод, поэтому я могу использовать такую функцию, как withSnakeCaseMemberNames.
1 ответ
Это ваша ошибка:
implicit val columnTypeDecoder: Decoder[AttributeType] = deriveConfiguredDecoder
implicit val columnTypeEncoder: Encoder[AttributeType] = deriveConfiguredEncoder
Это приведет к появлению новых кодеков, лечению AttributeType
как и любой другой запечатанный признак, поэтому он будет использовать значение дискриминации (полуавтоматический режим всегда игнорирует существующие кодеки того типа, который они производят!).
Итак, вы получаете новый AttributeType
кодеков, поместите их в область, где они используются, и таким образом сделайте эти новые реализации более приоритетными, чем реализации из сопутствующего объекта. Более близкое неявное всегда побеждает.
Если вы не будете выводить кодеки (потому что уже существуют реализации, предоставленные CirceEnum
trait), тогда он будет работать так, как вы ожидаете.
Кроме того, вместо этого:
implicit val columnMetaDecoder: Decoder[ColumnMetadata] = deriveConfiguredDecoder
implicit val columnMetaEncoder: Encoder[ColumnMetadata] = deriveConfiguredEncoder
вы можете просто сделать это:
// 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)
Это избавит вас от необходимости создавать пакет кодеков и импортировать их вручную везде, где они вам нужны. Например
// 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
}