本文介绍了使用 ToolBox 反射时隐式解析失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据类路径在反射中生成 Avro4s 的 RecordFormat.以下代码抛出错误.

case class MyCaseClass(a: Int)println(toolBox.compile {工具箱.parse(s"""|导入 com.sksamuel.avro4s._|导入 mypackage.MyCaseClass|记录格式[MyCaseClass]|""".stripMargin)}())

无法找到 com.sksamuel.avro4s.Decoder[mypackage.MyCaseClass] 类型的证据参数的隐式值

RecordFormat 就像

object RecordFormat {def apply[T <: Product : Encoder : Decoder : SchemaFor]: RecordFormat[T] = apply(AvroSchema[T])def apply[T <: Product : Encoder : Decoder](schema: Schema): RecordFormat[T] = new RecordFormat[T] {私有 val fromRecord = FromRecord[T](schema)私有 val toRecord = ToRecord[T](schema)覆盖 def from(record: GenericRecord): T = fromRecord.from(record)覆盖 def to(t: T): Record = toRecord.to(t)}}

参考:https://github.com/sksamuel/avro4s/blob/release/2.0.x/avro4s-core/src/main/scala/com/sksamuel/avro4s/RecordFormat.scala

我可以看到,它可以解析 Encoder[MyCaseClass]SchemaFor[MyCaseClass],但无法解析 Decoder[MyCaseClass].>

同样的代码无需反射即可解析RecordFormat[MyCaseClass].

我可以看到Decoder是用类似于Encoder的宏实现的.

implicit def applyMacro[T 

为什么反射不能解决隐性证据?

解决方案

avro4s 4.x 使用 Magnoliaavro4s 2.x 使用原始 隐式宏 + Shapeless.

通常在运行时使用反射实现类型类不应该有重大问题工具箱,即使类型类是用宏定义的.

现在的问题是定义 com.sksamuel.avro4s.Decoder 的宏有一个错误.该行 Decoder.scala#L404

c.Expr[Decoder[T]](q""新 _root_.com.sksamuel.avro4s.Decoder[$tpe] {private[this] val 解码器 = Array(..$decoders)覆盖 def 解码(值:任何,架构:_root_.org.apache.avro.Schema):$tpe = {val fullName = $fullName值匹配{案例记录:_root_.org.apache.avro.generic.GenericRecord =>$companion.apply(..$fields)案例_ =>sys.error("此解码器解码 GenericRecord => " + fullName + "但已使用 "+ value 调用)}}}")

指的是 sys.error 而不是 卫生 _root_.scala.sys.error.

如果你修复了这一行,Decoder[MyCaseClass]RecordFormat[MyCaseClass] 将在工具箱中工作

println(toolBox.compile {工具箱.parse(s"""|导入 com.sksamuel.avro4s._|导入 mypackage.MyCaseClass|记录格式[MyCaseClass]|""".stripMargin)}())//com.sksamuel.avro4s.RecordFormat$$anon$1@25109d84

所以快速解决方法是删除该行

libraryDependencies +=com.sksamuel.avro4s"%%avro4s-core"%2…………"

build.sbt中添加

libraryDependencies += "com.chuusai";%% 无形"%2.3.3"libraryDependencies += "org.apache.avro";%avro"%1.8.2"

(否则你将得到 NoClassDefFoundError)并将以下打过补丁的 jars 放入 lib

https://github.com/DmytroMitin/avro4s-2.0.5-2.11-已修补

avro4s-core_2.11-2.0.5-SNAPSHOT.jaravro4s-macros_2.11-2.0.5-SNAPSHOT.jar

如果你像这样创建的话,你总是可以调试使用工具箱生成的基于隐式或基于宏的代码

val toolBox = runtimeMirror.mkToolBox(前端=新前端{覆盖 def display(info: Info): Unit = println(info)覆盖定义交互():单位 = ???},选项=-Xlog-隐含";//或-Xlog-implicits -Ymacro-debug-lite")

如果你这样做

println(reify{解码器[MyCaseClass]}.树)

打印

Decoder.apply[MyCaseClass](Decoder.applyMacro)

如此隐含的 Decoder[MyCaseClass] 被解析为 Decoder.applyMacro[MyCaseClass].

使用原始未打补丁的罐子

toolBox.compile {工具箱.parse(s"""|导入 com.sksamuel.avro4s._|导入 mypackage.MyCaseClass|Decoder.applyMacro[MyCaseClass]|""".stripMargin)}()

扔了

scala.tools.reflect.ToolBoxError:反射编译失败:对象错误不是包 sys 的成员

I am trying to generate Avro4s's RecordFormat in reflection based on class path. The following code throws an error.

case class MyCaseClass(a: Int)
println(toolBox.compile {
  toolBox.parse(
    s"""
       |import com.sksamuel.avro4s._
       |import mypackage.MyCaseClass
       |RecordFormat[MyCaseClass]
       |""".stripMargin
  )
}())

RecordFormat is like

object RecordFormat {

  def apply[T <: Product : Encoder : Decoder : SchemaFor]: RecordFormat[T] = apply(AvroSchema[T])

  def apply[T <: Product : Encoder : Decoder](schema: Schema): RecordFormat[T] = new RecordFormat[T] {
    private val fromRecord = FromRecord[T](schema)
    private val toRecord = ToRecord[T](schema)
    override def from(record: GenericRecord): T = fromRecord.from(record)
    override def to(t: T): Record = toRecord.to(t)
  }
}

Ref: https://github.com/sksamuel/avro4s/blob/release/2.0.x/avro4s-core/src/main/scala/com/sksamuel/avro4s/RecordFormat.scala

I can see, it can resolve Encoder[MyCaseClass] and SchemaFor[MyCaseClass] but fails for Decoder[MyCaseClass].

The same code can resolve RecordFormat[MyCaseClass] without reflection.

I can see that Decoder is implemented with macro similar to Encoder.

implicit def applyMacro[T <: Product]: Decoder[T] = macro applyMacroImpl[T]

Why reflection cannot resolve the implicit evidence?

解决方案

avro4s 4.x uses Magnolia but avro4s 2.x uses raw implicit macros + Shapeless.

Normally there shouldn't be significant problems with materializing a type class at runtime using reflective toolbox even if the type class is defined with macros.

The issue is now that the macro defining com.sksamuel.avro4s.Decoder has a bug. The line Decoder.scala#L404

c.Expr[Decoder[T]](
  q"""
  new _root_.com.sksamuel.avro4s.Decoder[$tpe] {
    private[this] val decoders = Array(..$decoders)

    override def decode(value: Any, schema: _root_.org.apache.avro.Schema): $tpe = {
      val fullName = $fullName
      value match {
        case record: _root_.org.apache.avro.generic.GenericRecord => $companion.apply(..$fields)
        case _ => sys.error("This decoder decodes GenericRecord => " + fullName + " but has been invoked with " + value)
      }
    }
  }
  """
)

refers to sys.error instead of hygienic _root_.scala.sys.error.

If you fix this line, Decoder[MyCaseClass] and RecordFormat[MyCaseClass] will work inside toolbox

println(toolBox.compile {
  toolBox.parse(
    s"""
       |import com.sksamuel.avro4s._
       |import mypackage.MyCaseClass
       |RecordFormat[MyCaseClass]
       |""".stripMargin
  )
}()) //com.sksamuel.avro4s.RecordFormat$$anon$1@25109d84

So a fast fix is to remove the line

libraryDependencies += "com.sksamuel.avro4s" %% "avro4s-core" % "2........."

in build.sbt, add

libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
libraryDependencies += "org.apache.avro" % "avro" % "1.8.2"

(otherwise you'll have NoClassDefFoundError) and put the following patched jars into lib

https://github.com/DmytroMitin/avro4s-2.0.5-2.11-patched

avro4s-core_2.11-2.0.5-SNAPSHOT.jar
avro4s-macros_2.11-2.0.5-SNAPSHOT.jar

You can always debug implicit-based or macro-based code generated with toolbox if you create it like

val toolBox = runtimeMirror.mkToolBox(
  frontEnd = new FrontEnd {
    override def display(info: Info): Unit = println(info)
    override def interactive(): Unit = ???
  },
  options = "-Xlog-implicits" // or "-Xlog-implicits -Ymacro-debug-lite"
)

If you do

println(reify{
  Decoder[MyCaseClass]
}.tree)

it prints

Decoder.apply[MyCaseClass](Decoder.applyMacro)

so implicit Decoder[MyCaseClass] is resolved as Decoder.applyMacro[MyCaseClass].

With the original unpatched jars

toolBox.compile {
  toolBox.parse(
    s"""
       |import com.sksamuel.avro4s._
       |import mypackage.MyCaseClass
       |Decoder.applyMacro[MyCaseClass]
       |""".stripMargin
  )
}()

threw

scala.tools.reflect.ToolBoxError: reflective compilation has failed:
object error is not a member of package sys

这篇关于使用 ToolBox 反射时隐式解析失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-22 07:11