Сериализация 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
}
Другие вопросы по тегам