Hier ist über die einfachste Art der ich denken kann, den Singular Teil allgemein mit circe zu tun:
import io.circe.{ Decoder, Encoder, Json }
import io.circe.generic.encoding.DerivedObjectEncoder
trait GenericResource {
val singularName: String
val pluralName: String
}
object GenericResource {
implicit def encodeResource[A <: GenericResource](implicit
derived: DerivedObjectEncoder[A]
): Encoder[A] = Encoder.instance { a =>
Json.obj(a.singularName -> derived(a))
}
}
Und dann, wenn Sie einige Fallklasse haben GenericResource
wie diese erstreckt:
case class Product(name: String) extends GenericResource {
val singularName = "product"
val pluralName = "products"
}
Sie kann dies tun (vorausgesetzt, dass alle Mitglieder der Fallklasse codierbar sind):
scala> import io.circe.syntax._
import io.circe.syntax._
scala> Product("car").asJson.noSpaces
res0: String = {"product":{"name":"car"}}
Kein vorformulierten, keine zusätzlichen Importe usw.
Der Seq
Fall ist ein wenig komplizierter, da circe einen Seq[A]
Encoder für jede A
liefert automatisch die eine Encoder
hat, aber es nicht tut, was Sie wollen-es einfach codiert die Objekte und steckt sie in ein JSON-Array. Sie können so etwas schreiben:
implicit def encodeResources[A <: GenericResource](implicit
derived: DerivedObjectEncoder[A]
): Encoder[Seq[A]] = Encoder.instance {
case values @ (head +: _) =>
Json.obj(head.pluralName -> Encoder.encodeList(derived)(values.toList))
case Nil => Json.obj()
}
Und es wie folgt verwenden:
scala> Seq(Product("car"), Product("truck")).asJson.noSpaces
res1: String = {"products":[{"name":"car"},{"name":"truck"}]}
Aber man kann nicht einfach kleben Sie es in dem Begleitobjekt und erwarten, alles zu arbeiten-Sie haben es zu setzen irgendwo und importieren Sie es, wenn Sie es brauchen (ansonsten hat es die gleiche Priorität wie die Standard Seq[A]
Instanzen).
scala> Seq.empty[Product].asJson.noSpaces
res2: String = {}
Dies liegt daran, dass der Plural Name der Ressource auf Instanzebene angebracht ist, und wenn Sie: wenn die Seq
ist leer
Ein weiteres Problem mit dieser encodeResources
Implementierung ist, dass es nur ein leeres Objekt zurück habe keine Instanz, es gibt keine Möglichkeit, es zu bekommen (kurz vor dem Nachdenken). Sie könnten natürlich eine falsche Instanz heraufbeschwören, indem Sie Nullen an den Konstrukteur oder Ähnliches übergeben, aber das scheint außerhalb des Umfangs dieser Frage zu liegen.
Dieses Problem (die Ressourcennamen, die an Instanzen angehängt werden) wird ebenfalls problematisch, wenn Sie diesen JSON dekodieren müssen, den Sie codiert haben. Wenn das der Fall ist, würde ich vorschlagen, einen etwas anderen Ansatz zu wählen, bei dem Sie beispielsweise ein GenericResourceCompanion
Merkmal haben, das Sie in das Begleitobjekt für den spezifischen Ressourcentyp mischen und die Namen dort angeben. Wenn das keine Option ist, sind Sie wahrscheinlich mit Reflektions- oder Fake-Instanzen oder beidem beschäftigt (aber wiederum wahrscheinlich nicht im Umfang für diese Frage).
Wow, danke für die detaillierte Lösung. –