Circe: Snake Case (oder ein konfigurationsbezogenes Problem) mit Scala 3

Erstellt am 10. März 2021  ·  4Kommentare  ·  Quelle: circe/circe

Hallo, und vielen Dank für all die Bemühungen, die Sie in die Migration von Circe und Scala 3 investieren.

Ich habe versucht, den Aufwand zu messen, der erforderlich ist, um eines meiner Projekte auf Scala3 zu migrieren.

Ich habe ein bisschen Probleme mit der JSON-Serialisierung, da es Snake_case-Transformationen für Fallklassenattribute beinhaltet.
Früher habe ich circe-generic-extras mit ConfiguredJsonObject verwendet, aber ich habe gesehen, dass es wahrscheinlich nicht so bald auf Scala3 aktualisiert wird, was ich verstehen kann.

Gibt es eine andere Möglichkeit (außer dem Schreiben eines Encoders von Hand), um einen Encoder zu erhalten, der snake_case für Fallklassenattribute verwendet?

Wie einen Encoder abzuleiten und ihn irgendwie "zuzuordnen"?

Es tut mir leid, wenn die Frage nicht klar genug ist, aber ich versuche, eine Problemumgehung dafür zu finden, und nochmals vielen Dank für Ihre Zeit und Mühe.

Hilfreichster Kommentar

Richtig, ich denke, leider ist Ihre beste Wahl für Scala 3 derzeit so etwas (getestet auf Scala 3):

scala> import io.circe.{Encoder, JsonObject}, io.circe.generic.semiauto.deriveEncoder

scala> import java.util.regex.Pattern

scala> val snakeCaseTransformation: String => String = s => {
     |   val basePattern: Pattern = Pattern.compile("([A-Z]+)([A-Z][a-z])")
     |   val swapPattern: Pattern = Pattern.compile("([a-z\\d])([A-Z])")
     |   val partial = basePattern.matcher(s).replaceAll("$1_$2")
     |   swapPattern.matcher(partial).replaceAll("$1_$2").toLowerCase
     | }
val snakeCaseTransformation: String => String = Lambda$6404/700040865<strong i="6">@2b523359</strong>

scala> def snakeCaseIfy[A](encoder: Encoder.AsObject[A]): Encoder.AsObject[A] =
     |   encoder.mapJsonObject(obj =>
     |     JsonObject.fromIterable(obj.toIterable.map {
     |       case (k, v) => (snakeCaseTransformation(k), v) }
     |     )
     |   )
     | 
def snakeCaseIfy
  [A](encoder: io.circe.Encoder.AsObject[A]): io.circe.Encoder.AsObject[A]

scala> case class Foo(firstName: String, lastName: String)
// defined case class Foo

scala> implicit val encoderFoo: Encoder.AsObject[Foo] = snakeCaseIfy(deriveEncoder)
val encoderFoo: io.circe.Encoder.AsObject[Foo] = io.circe.Encoder$$anon$66<strong i="7">@717a0dd4</strong>

scala> import io.circe.syntax._

scala> Foo("Foo", "McBar").asJson
val res0: io.circe.Json = {
  "first_name" : "Foo",
  "last_name" : "McBar"
}

Alle 4 Kommentare

Sie könnten mapJson für ein abgeleitetes Encoder und eine Snake_case-Transformation für jeden Schlüssel in diesem Json Objekt ausführen.

Richtig, ich denke, leider ist Ihre beste Wahl für Scala 3 derzeit so etwas (getestet auf Scala 3):

scala> import io.circe.{Encoder, JsonObject}, io.circe.generic.semiauto.deriveEncoder

scala> import java.util.regex.Pattern

scala> val snakeCaseTransformation: String => String = s => {
     |   val basePattern: Pattern = Pattern.compile("([A-Z]+)([A-Z][a-z])")
     |   val swapPattern: Pattern = Pattern.compile("([a-z\\d])([A-Z])")
     |   val partial = basePattern.matcher(s).replaceAll("$1_$2")
     |   swapPattern.matcher(partial).replaceAll("$1_$2").toLowerCase
     | }
val snakeCaseTransformation: String => String = Lambda$6404/700040865<strong i="6">@2b523359</strong>

scala> def snakeCaseIfy[A](encoder: Encoder.AsObject[A]): Encoder.AsObject[A] =
     |   encoder.mapJsonObject(obj =>
     |     JsonObject.fromIterable(obj.toIterable.map {
     |       case (k, v) => (snakeCaseTransformation(k), v) }
     |     )
     |   )
     | 
def snakeCaseIfy
  [A](encoder: io.circe.Encoder.AsObject[A]): io.circe.Encoder.AsObject[A]

scala> case class Foo(firstName: String, lastName: String)
// defined case class Foo

scala> implicit val encoderFoo: Encoder.AsObject[Foo] = snakeCaseIfy(deriveEncoder)
val encoderFoo: io.circe.Encoder.AsObject[Foo] = io.circe.Encoder$$anon$66<strong i="7">@717a0dd4</strong>

scala> import io.circe.syntax._

scala> Foo("Foo", "McBar").asJson
val res0: io.circe.Json = {
  "first_name" : "Foo",
  "last_name" : "McBar"
}

Super ausführliche Hilfe.
Danke schön!

Es tut mir leid, dass ich nur das Serialisierungsproblem aufgedeckt habe, obwohl ich offensichtlich Deserialisierungsprobleme habe.

Gibt es eine Möglichkeit, mit einem abgeleitetenDecoder "unsakify" zu machen?

Edit: https://gitter.im/circe/circe?at=5d542e282612bb718c685031 dieses withFocus scheint ein guter Ausgangspunkt für meine Nachforschungen zu sein.

Etwas um diese Zeilen:

    private val snakePattern = "_([a-z\\d])".r

    private val snakeToCamel: String => String = s => {
        snakePattern.replaceAllIn(s, {m =>
            m.group(1).toUpperCase()
        })
    }

    private def unSnakeCaseIfy(o: JsonObject): JsonObject = 
        JsonObject.fromIterable(o.toVector.map { 
            case (k, v) => snakeToCamel(k) -> v 
        })

    def unSnakeCaseIfy[A](decoder: Decoder[A]): Decoder[A] = (c: HCursor) => {
        decoder.tryDecode(c.withFocus(_.mapObject(unSnakeCaseIfy)))
    }
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen