json: codifica objetos anidados como una cadena

CorePress2024-01-25  156

Dadas las siguientes clases de casos:

case class Mailbox(value: String)
case class Group(objectType: String, mailbox: Mailbox)

Estoy intentando encontrar una manera de codificar un objeto de grupo de la siguiente manera, donde el buzón se codifica como un valor de cadena, en lugar de un objeto:

{
  "objectType" : "Group",
  "mailbox" : "mailto:[email protected]"
}

Con la derivación automática, tanto la codificación como la decodificación tienen éxito, pero termino con un resultado como el siguiente, como era de esperar:

{
  "objectType" : "Group",
  "mailbox" : {
    "value" : "mailto:[email protected]"
  }
}

Puedo lograr el resultado que quiero agregando un codificador personalizado como el siguiente:

object Mailbox {
  implicit val encoder: Encoder[Mailbox] = (m: Mailbox) => Json.fromString(m.value)
  implicit val decoder: Decoder[Mailbox] = deriveDecoder[Mailbox]
}

Sin embargo, la decodificación falla con lo siguiente:

DecodingFailure(Attempt to decode value on failed cursor, List(DownField(value), DownField(mailbox)))

Intenté resolver este problema escribiendo también un descodificador personalizado para Mailbox, pero obtuve el mismo resultado. Se agradecería cualquier orientación sobre la forma correcta de abordar esta situación.

Aquí está el código completo:

case class Mailbox(value: String)
object Mailbox {
  implicit val encoder: Encoder[Mailbox] = (m: Mailbox) => Json.fromString(m.value)
  implicit val decoder: Decoder[Mailbox] = deriveDecoder[Mailbox]
}

case class Group(objectType: String, mailbox: Mailbox)

object Sandbox {
  def main(args: Array[String]): Unit = {

    val group: Group = Group("Group", Mailbox("mailto:[email protected]"))
    val json: String = group.asJson.spaces2
    println(json)

    parser.decode[Group](json) match {
      case Right(group) => println(group)
      case Left(err) => println(err)
    }
  }
}

Tenga en cuenta que este es un ejemplo derivado, que significa únicamentepara demostrar mi pregunta.

1

Si convierte esa clase en una clase de valor y usa circe-generic-extras, funcionará de inmediato. Pero tenga en cuenta que el uso de clases de Valor puede causar problemas de rendimiento.

– Luis Miguel Mejía Suárez

28/03/2021 a las 16:57



------------------------------------

Como alternativa para crear codificadores/decodificadores personalizados, también puedes usar derivaUnwrappedEncoder/deriveUnwrappedDecoder de circe extras.

Simplemente agregue la siguiente importación en su buid.sbt:

libraryDependencies += "io.circe" %% "circe-generic-extras" % "xxx"

y luego podrás hacer:

import io.circe.generic.extras.semiauto._

implicit val encoder: Encoder[Mailbox] = deriveUnwrappedEncoder
implicit val decoder: Decoder[Mailbox] = deriveUnwrappedDecoder



------------------------------------

Puedes usar map / contramap en su lugar para asignar tu tipo a String y viceversa:

object Mailbox {

  implicit val encoder: Encoder[Mailbox] = Encoder.encodeString.contramap[Mailbox](_.value)
  implicit val decoder: Decoder[Mailbox] = Decoder.decodeString.map[Mailbox](Mailbox.apply)
}

Hay documentación que describe casi exactamente este escenario en Codificadores/decodificadores personalizados

0

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare