【问题标题】:Type parameter for implicit valued method in Scala - CirceScala 中隐式值方法的类型参数 - Circe
【发布时间】:2022-11-13 04:12:14
【问题描述】:

我是 Scala 新手,使用 circe 对一些 API 响应进行建模和序列化。 我发现自己使用以下样板

sealed trait SomeTrait

object SomeTrait {
    implicit val someEncoder: Encoder[SomeTrait] = deriveEncoder[SomeTrait]
    implicit val someDecoder: Decoder[SomeTrait] = deriveDecoder[SomeTrait]

    <code>
}

相反,我想使用泛型,并定义类似

trait SerializableTrait[A] {
    implicit val someEncoder: Encoder[A] = deriveEncoder[A]
    implicit val someDecoder: Decoder[A] = deriveDecoder[A]
}

然后只需多次使用扩展它:

sealed trait SomeTrait

object SomeTrait extends SerializableTrait[SomeTrain] {

    <code>
}

但我得到could not find Lazy implicit value of type io.circe.generic.encoding.DerivedAsObjectEncoder 和解码器类似。

我知道我可能正在尝试实现circle.auto 功能,但我想了解这种用法​​有什么问题。理想情况下,我希望编译器仅在实际需要时在非类型参数化特征内评估dervieEncoder/Decoder

【问题讨论】:

  • 如果这可行,那么要么会提供它,或者更确切地说,我们根本不需要派生任何东西。 - 派生发生在编译时,只适用于某些特定类型的案例类,这就是你必须调用它的原因。

标签: scala generics implicit circe


【解决方案1】:

您的问题类似于 Implicit Json Formatter for value classes in Scala 。就在那里 json 库是 play-json 并且您正在使用 Circe。

deriveEncoderderiveDecodermacros (实际上不是它们本身,而是它们调用宏)。所以你不能在你想要的地方打电话给他们。 deriveEncoder[A]deriveDecoder[A] 正在尝试扩展 A 不是案例类或密封特征(具有案例类子项)而是抽象类型(类型参数)。

将一些通用代码提取到特征并扩展此特征是 OOP 避免代码重复的方法,但宏是一种不同的范式,即元编程。使用元编程,您不能总是遵循 OOP 原则。

  • 如果您想推迟宏的扩展,您可以将您的隐式设为implicit macros。这有所不同,因为在您的代码中 deriveEncoder[A]deriveDecoder[A] 现在被调用(扩展)抽象 A 但是使用隐式宏,它们将在稍后请求隐式时调用(扩展),即当 A 被推断为一个具体的密封特征或案例类。
// in a different subproject

import io.circe.{Decoder, Encoder}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox // libraryDependencies += scalaOrganization.value % "scala-reflect" % scalaVersion.value

trait SerializableTrait[A] {
  implicit def someEncoder: Encoder[A] = macro SerializableTraitMacros.someEncoderImpl[A]
  implicit def someDecoder: Decoder[A] = macro SerializableTraitMacros.someDecoderImpl[A]
}

class SerializableTraitMacros(val c: whitebox.Context) {
  import c.universe._

  val semiauto = q"_root_.io.circe.generic.semiauto"

  def someEncoderImpl[A: WeakTypeTag]: Tree = q"$semiauto.deriveEncoder[${weakTypeOf[A]}]"
  def someDecoderImpl[A: WeakTypeTag]: Tree = q"$semiauto.deriveDecoder[${weakTypeOf[A]}]"
}

现在您不需要在案例类或密封特征的伴随对象中定义隐式。您只需要根据需要使伴随对象扩展 SerializableTrait

import io.circe.{Decoder, Encoder}

sealed trait SomeTrait

object SomeTrait extends SerializableTrait[SomeTrait]

case class A(i: Int, s: String) extends SomeTrait // the sealed trait must have at least one case-class child

implicitly[Encoder[SomeTrait]] // compiles
implicitly[Decoder[SomeTrait]] // compiles

使用scalacOptions += "-Ymacro-debug-lite"(也许"-Xlog-implicits" 也很有用),您可以查看Circe 实际为该特征生成的编解码器实例

//scalac: {
//  val inst$macro$14: io.circe.generic.encoding.DerivedAsObjectEncoder[App.SomeTrait] = {
//    final class anon$someEncoder$macro$13 extends _root_.scala.Serializable {
//      def <init>() = {
//        super.<init>();
//        ()
//      };
//      lazy val inst$macro$1: io.circe.generic.encoding.DerivedAsObjectEncoder[App.SomeTrait] = encoding.this.DerivedAsObjectEncoder.deriveEncoder[App.SomeTrait, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](shapeless.this.LabelledGeneric.materializeCoproduct[App.SomeTrait, (Symbol @@ String("A")) :: shapeless.HNil, App.A :+: shapeless.CNil, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](DefaultSymbolicLabelling.instance[App.SomeTrait, (Symbol @@ String("A")) :: shapeless.HNil](::.apply[Symbol @@ String("A"), shapeless.HNil.type](scala.Symbol.apply("A").asInstanceOf[Symbol @@ String("A")], HNil)), Generic.instance[App.SomeTrait, App.A :+: shapeless.CNil](((p: App.SomeTrait) => Coproduct.unsafeMkCoproduct((p: p: @_root_.scala.unchecked) match {
//  case (_: App.A) => 0
//}, p).asInstanceOf[App.A :+: shapeless.CNil]), ((x$1: App.A :+: shapeless.CNil) => Coproduct.unsafeGet(x$1).asInstanceOf[App.SomeTrait])), coproduct.this.ZipWithKeys.cpZipWithKeys[Symbol @@ String("A"), App.A, shapeless.HNil, shapeless.CNil](coproduct.this.ZipWithKeys.cnilZipWithKeys, Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("A")]](scala.Symbol.apply("A").asInstanceOf[Symbol @@ String("A")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("A")]])), scala.this.<:<.refl[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]), shapeless.Lazy.apply[io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]](inst$macro$2)).asInstanceOf[io.circe.generic.encoding.DerivedAsObjectEncoder[App.SomeTrait]];
//      lazy val inst$macro$2: io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] = ({
//  final class $anon extends io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    private[this] val circeGenericEncoderForA = shapeless.Lazy.apply[io.circe.generic.encoding.DerivedAsObjectEncoder[App.A]](inst$macro$3).value;
//    final def encodeObject(a: shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out): io.circe.JsonObject = shapeless.Inr.apply[Nothing, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](a) match {
//      case shapeless.Inr((circeGenericInrBindingForA @ _)) => circeGenericInrBindingForA match {
//        case shapeless.Inl((circeGenericInlBindingForA @ _)) => io.circe.JsonObject.singleton("A", $anon.this.circeGenericEncoderForA.apply(circeGenericInlBindingForA))
//        case shapeless.Inr(_) => scala.sys.`package`.error("Cannot encode CNil")
//      }
//    }
//  };
//  new $anon()
//}: io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]).asInstanceOf[io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]];
//      lazy val inst$macro$3: io.circe.generic.encoding.DerivedAsObjectEncoder[App.A] = encoding.this.DerivedAsObjectEncoder.deriveEncoder[App.A, shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](shapeless.this.LabelledGeneric.materializeProduct[App.A, (Symbol @@ String("i")) :: (Symbol @@ String("s")) :: shapeless.HNil, Int :: String :: shapeless.HNil, shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](DefaultSymbolicLabelling.instance[App.A, (Symbol @@ String("i")) :: (Symbol @@ String("s")) :: shapeless.HNil](::.apply[Symbol @@ String("i"), (Symbol @@ String("s")) :: shapeless.HNil.type](scala.Symbol.apply("i").asInstanceOf[Symbol @@ String("i")], ::.apply[Symbol @@ String("s"), shapeless.HNil.type](scala.Symbol.apply("s").asInstanceOf[Symbol @@ String("s")], HNil))), Generic.instance[App.A, Int :: String :: shapeless.HNil](((x0$3: App.A) => x0$3 match {
//  case App.this.A((i$macro$10 @ _), (s$macro$11 @ _)) => ::.apply[Int, String :: shapeless.HNil.type](i$macro$10, ::.apply[String, shapeless.HNil.type](s$macro$11, HNil)).asInstanceOf[Int :: String :: shapeless.HNil]
//}), ((x0$4: Int :: String :: shapeless.HNil) => x0$4 match {
//  case ::((i$macro$8 @ _), ::((s$macro$9 @ _), HNil)) => App.this.A.apply(i$macro$8, s$macro$9)
//})), hlist.this.ZipWithKeys.hconsZipWithKeys[Symbol @@ String("i"), Int, (Symbol @@ String("s")) :: shapeless.HNil, String :: shapeless.HNil, shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](hlist.this.ZipWithKeys.hconsZipWithKeys[Symbol @@ String("s"), String, shapeless.HNil, shapeless.HNil, shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](hlist.this.ZipWithKeys.hnilZipWithKeys, Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("s")]](scala.Symbol.apply("s").asInstanceOf[Symbol @@ String("s")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("s")]])), Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("i")]](scala.Symbol.apply("i").asInstanceOf[Symbol @@ String("i")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("i")]])), scala.this.<:<.refl[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]), shapeless.Lazy.apply[io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]](inst$macro$12)).asInstanceOf[io.circe.generic.encoding.DerivedAsObjectEncoder[App.A]];
//      lazy val inst$macro$12: io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] = ({
//  final class $anon extends io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    private[this] val circeGenericEncoderFori = circe.this.Encoder.encodeInt;
//    private[this] val circeGenericEncoderFors = circe.this.Encoder.encodeString;
//    final def encodeObject(a: shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out): io.circe.JsonObject = a match {
//      case shapeless.::((circeGenericHListBindingFori @ _), shapeless.::((circeGenericHListBindingFors @ _), shapeless.HNil)) => io.circe.JsonObject.fromIterable(scala.collection.immutable.Vector.apply[(String, io.circe.Json)](scala.Tuple2.apply[String, io.circe.Json]("i", $anon.this.circeGenericEncoderFori.apply(circeGenericHListBindingFori)), scala.Tuple2.apply[String, io.circe.Json]("s", $anon.this.circeGenericEncoderFors.apply(circeGenericHListBindingFors))))
//    }
//  };
//  new $anon()
//}: io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]).asInstanceOf[io.circe.generic.encoding.ReprAsObjectEncoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]]
//    };
//    new anon$someEncoder$macro$13().inst$macro$1
//  };
//  _root_.shapeless.Lazy.apply[io.circe.generic.encoding.DerivedAsObjectEncoder[App.SomeTrait]](inst$macro$14)
//}

//scalac: {
//  val inst$macro$14: io.circe.generic.decoding.DerivedDecoder[App.SomeTrait] = {
//    final class anon$someDecoder$macro$13 extends _root_.scala.Serializable {
//      def <init>() = {
//        super.<init>();
//        ()
//      };
//      lazy val inst$macro$1: io.circe.generic.decoding.DerivedDecoder[App.SomeTrait] = decoding.this.DerivedDecoder.deriveDecoder[App.SomeTrait, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](shapeless.this.LabelledGeneric.materializeCoproduct[App.SomeTrait, (Symbol @@ String("A")) :: shapeless.HNil, App.A :+: shapeless.CNil, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](DefaultSymbolicLabelling.instance[App.SomeTrait, (Symbol @@ String("A")) :: shapeless.HNil](::.apply[Symbol @@ String("A"), shapeless.HNil.type](scala.Symbol.apply("A").asInstanceOf[Symbol @@ String("A")], HNil)), Generic.instance[App.SomeTrait, App.A :+: shapeless.CNil](((p: App.SomeTrait) => Coproduct.unsafeMkCoproduct((p: p: @_root_.scala.unchecked) match {
//  case (_: App.A) => 0
//}, p).asInstanceOf[App.A :+: shapeless.CNil]), ((x$1: App.A :+: shapeless.CNil) => Coproduct.unsafeGet(x$1).asInstanceOf[App.SomeTrait])), coproduct.this.ZipWithKeys.cpZipWithKeys[Symbol @@ String("A"), App.A, shapeless.HNil, shapeless.CNil](coproduct.this.ZipWithKeys.cnilZipWithKeys, Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("A")]](scala.Symbol.apply("A").asInstanceOf[Symbol @@ String("A")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("A")]])), scala.this.<:<.refl[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]), shapeless.Lazy.apply[io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]](inst$macro$2)).asInstanceOf[io.circe.generic.decoding.DerivedDecoder[App.SomeTrait]];
//      lazy val inst$macro$2: io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] = ({
//  final class $anon extends io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    private[this] val circeGenericDecoderForA = shapeless.Lazy.apply[io.circe.generic.decoding.DerivedDecoder[App.A]](inst$macro$3).value;
//    final def apply(c: io.circe.HCursor): io.circe.Decoder.Result[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] = {
//      val result = c.downField("A");
//      if (result.succeeded)
//        scala.Some.apply[io.circe.Decoder.Result[App.A]]($anon.this.circeGenericDecoderForA.tryDecode(result))
//      else
//        scala.None
//    } match {
//      case scala.Some((result @ _)) => result match {
//        case scala.util.Right((v @ _)) => scala.util.Right.apply[Nothing, shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](ReprDecoder.injectLeftValue[Symbol @@ String("A"), App.A, shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](v))
//        case scala.util.Left((err @ _)) => scala.util.Left.apply[io.circe.DecodingFailure, Nothing](err)
//      }
//      case scala.None => (scala.util.Left.apply[io.circe.DecodingFailure, shapeless.CNil](io.circe.DecodingFailure.apply("JSON decoding to CNil should never happen", c.history)): scala.util.Either[io.circe.DecodingFailure, shapeless.CNil]) match {
//        case scala.util.Right((v @ _)) => scala.util.Right.apply[Nothing, shapeless.Inr[Nothing,shapeless.CNil]](shapeless.Inr.apply[Nothing, shapeless.CNil](v))
//        case scala.util.Left((err @ _)) => scala.util.Left.apply[io.circe.DecodingFailure, Nothing](err)
//      }
//    };
//    final override def decodeAccumulating(c: io.circe.HCursor): io.circe.Decoder.AccumulatingResult[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out] = {
//      val result = c.downField("A");
//      if (result.succeeded)
//        scala.Some.apply[io.circe.Decoder.AccumulatingResult[App.A]]($anon.this.circeGenericDecoderForA.tryDecodeAccumulating(result))
//      else
//        scala.None
//    } match {
//      case scala.Some((result @ _)) => result.map[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](((v: App.A) => ReprDecoder.injectLeftValue[Symbol @@ String("A"), App.A, shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out](v)))
//      case scala.None => cats.data.Validated.invalidNel[io.circe.DecodingFailure, shapeless.CNil](io.circe.DecodingFailure.apply("JSON decoding to CNil should never happen", c.history)).map[shapeless.Inr[Nothing,shapeless.CNil]](((x$3: shapeless.CNil) => shapeless.Inr.apply[Nothing, shapeless.CNil](x$3)))
//    }
//  };
//  new $anon()
//}: io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]).asInstanceOf[io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("A"),App.A] :+: shapeless.ops.coproduct.ZipWithKeys.cnilZipWithKeys.Out]];
//      lazy val inst$macro$3: io.circe.generic.decoding.DerivedDecoder[App.A] = decoding.this.DerivedDecoder.deriveDecoder[App.A, shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](shapeless.this.LabelledGeneric.materializeProduct[App.A, (Symbol @@ String("i")) :: (Symbol @@ String("s")) :: shapeless.HNil, Int :: String :: shapeless.HNil, shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](DefaultSymbolicLabelling.instance[App.A, (Symbol @@ String("i")) :: (Symbol @@ String("s")) :: shapeless.HNil](::.apply[Symbol @@ String("i"), (Symbol @@ String("s")) :: shapeless.HNil.type](scala.Symbol.apply("i").asInstanceOf[Symbol @@ String("i")], ::.apply[Symbol @@ String("s"), shapeless.HNil.type](scala.Symbol.apply("s").asInstanceOf[Symbol @@ String("s")], HNil))), Generic.instance[App.A, Int :: String :: shapeless.HNil](((x0$3: App.A) => x0$3 match {
//  case App.this.A((i$macro$10 @ _), (s$macro$11 @ _)) => ::.apply[Int, String :: shapeless.HNil.type](i$macro$10, ::.apply[String, shapeless.HNil.type](s$macro$11, HNil)).asInstanceOf[Int :: String :: shapeless.HNil]
//}), ((x0$4: Int :: String :: shapeless.HNil) => x0$4 match {
//  case ::((i$macro$8 @ _), ::((s$macro$9 @ _), HNil)) => App.this.A.apply(i$macro$8, s$macro$9)
//})), hlist.this.ZipWithKeys.hconsZipWithKeys[Symbol @@ String("i"), Int, (Symbol @@ String("s")) :: shapeless.HNil, String :: shapeless.HNil, shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](hlist.this.ZipWithKeys.hconsZipWithKeys[Symbol @@ String("s"), String, shapeless.HNil, shapeless.HNil, shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out](hlist.this.ZipWithKeys.hnilZipWithKeys, Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("s")]](scala.Symbol.apply("s").asInstanceOf[Symbol @@ String("s")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("s")]])), Witness.mkWitness[Symbol with shapeless.tag.Tagged[String("i")]](scala.Symbol.apply("i").asInstanceOf[Symbol @@ String("i")].asInstanceOf[Symbol with shapeless.tag.Tagged[String("i")]])), scala.this.<:<.refl[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]), shapeless.Lazy.apply[io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]](inst$macro$12)).asInstanceOf[io.circe.generic.decoding.DerivedDecoder[App.A]];
//      lazy val inst$macro$12: io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] = ({
//  final class $anon extends io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    private[this] val circeGenericDecoderFori = circe.this.Decoder.decodeInt;
//    private[this] val circeGenericDecoderFors = circe.this.Decoder.decodeString;
//    final def apply(c: io.circe.HCursor): io.circe.Decoder.Result[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] = ReprDecoder.consResults[io.circe.Decoder.Result, Symbol @@ String("i"), Int, shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]($anon.this.circeGenericDecoderFori.tryDecode(c.downField("i")), ReprDecoder.consResults[io.circe.Decoder.Result, Symbol @@ String("s"), String, shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]($anon.this.circeGenericDecoderFors.tryDecode(c.downField("s")), ReprDecoder.hnilResult)(io.circe.Decoder.resultInstance))(io.circe.Decoder.resultInstance);
//    final override def decodeAccumulating(c: io.circe.HCursor): io.circe.Decoder.AccumulatingResult[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out] = ReprDecoder.consResults[io.circe.Decoder.AccumulatingResult, Symbol @@ String("i"), Int, shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]($anon.this.circeGenericDecoderFori.tryDecodeAccumulating(c.downField("i")), ReprDecoder.consResults[io.circe.Decoder.AccumulatingResult, Symbol @@ String("s"), String, shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]($anon.this.circeGenericDecoderFors.tryDecodeAccumulating(c.downField("s")), ReprDecoder.hnilResultAccumulating)(io.circe.Decoder.accumulatingResultInstance))(io.circe.Decoder.accumulatingResultInstance)
//  };
//  new $anon()
//}: io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]).asInstanceOf[io.circe.generic.decoding.ReprDecoder[shapeless.labelled.FieldType[Symbol @@ String("i"),Int] :: shapeless.labelled.FieldType[Symbol @@ String("s"),String] :: shapeless.ops.hlist.ZipWithKeys.hnilZipWithKeys.Out]]
//    };
//    new anon$someDecoder$macro$13().inst$macro$1
//  };
//  _root_.shapeless.Lazy.apply[io.circe.generic.decoding.DerivedDecoder[App.SomeTrait]](inst$macro$14)
//}

如果你想implicitly[Encoder[A]]implicitly[Decoder[A]]也编译你应该定义伴侣object A extends SerializableTrait[A]。所以最好以不同的方式调用SerializableTraitSerializableTraitOrClass或类似的名称。

  • 或者,您可以为类/特征或其伴随对象定义macro annotation。它将在伴随对象中生成必要的隐式
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.reflect.macros.blackbox
import scala.language.experimental.macros

@compileTimeOnly("""enable macro annotations: scalacOptions += "-Ymacro-annotations" """)
class serializableTrait extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro SerializableTraitMacros.macroTransformImpl
}

object SerializableTraitMacros {
  def macroTransformImpl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    val circe = q"_root_.io.circe"
    val semiauto = q"$circe.generic.semiauto"

    def modifyObject(obj: Tree): Tree = obj match {
      case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" =>
        val className = tname.toTypeName
        val encoder = TermName(c.freshName(s"${tname}Encoder"))
        val decoder = TermName(c.freshName(s"${tname}Decoder"))

        q"""$mods object $tname extends { ..$earlydefns } with ..$parents { $self =>
          ..$body
          implicit val $encoder: $circe.Encoder[$className] = $semiauto.deriveEncoder[$className]
          implicit val $decoder: $circe.Decoder[$className] = $semiauto.deriveDecoder[$className]
        }"""
    }

    def modify(cls: Tree, obj: Tree): Tree = q"..${Seq(cls, modifyObject(obj))}"

    annottees match {
      case (cls: ClassDef) :: (obj: ModuleDef) :: Nil => modify(cls, obj)
      case (cls: ClassDef) :: Nil => modify(cls, q"object ${cls.name.toTermName}")
      case (obj: ModuleDef) :: Nil => modifyObject(obj) // this works for the companion object of a sealed trait or top-level case class but not nested case class
    }
  }
}
import io.circe.{Decoder, Encoder}

@serializableTrait
sealed trait SomeTrait

case class A(i: Int, s: String) extends SomeTrait

implicitly[Encoder[SomeTrait]] // compiles
implicitly[Decoder[SomeTrait]] // compiles

//scalac: object SomeTrait extends scala.AnyRef {
//  def <init>() = {
//    super.<init>();
//    ()
//  };
//  implicit val SomeTraitEncoder$macro$1: _root_.io.circe.Encoder[SomeTrait] = _root_.io.circe.generic.semiauto.deriveEncoder[SomeTrait];
//  implicit val SomeTraitDecoder$macro$2: _root_.io.circe.Decoder[SomeTrait] = _root_.io.circe.generic.semiauto.deriveDecoder[SomeTrait]
//}

如果你想让implicitly[Encoder[A]]implicitly[Decoder[A]] 也编译,你也应该注释案例类A

现在实现了宏注释,以便您可以注释特征或其伴随对象(无论如何都会在伴随对象中生成隐含)但由于某种原因,如果您注释案例类的伴随对象(@ 987654351@)。这似乎是因为我测试了嵌套的SomeTraitA,对于顶级它没问题。

实际上,我们的宏注解类似于标准的@JsonCodec

https://circe.github.io/circe/codecs/semiauto-derivation.html#jsoncodec

import io.circe.generic.JsonCodec

@JsonCodec 
sealed trait SomeTrait

@JsonCodec 
case class A(i: Int, s: String) extends SomeTrait

//  object SomeTrait extends scala.AnyRef {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    implicit val codecForSomeTrait: AsObject[SomeTrait] = semiauto.deriveCodec[SomeTrait]
//  };
//  object A extends scala.AnyRef {
//    def <init>() = {
//      super.<init>();
//      ()
//    };
//    implicit val codecForA: AsObject[A] = semiauto.deriveCodec[A]
//  };

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    • 2019-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多