问题描述
我正在尝试根据类路径在反射中生成 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)}}
我可以看到,它可以解析 Encoder[MyCaseClass]
和 SchemaFor[MyCaseClass]
,但无法解析 Decoder[MyCaseClass]
.>
同样的代码无需反射即可解析RecordFormat[MyCaseClass]
.
我可以看到Decoder
是用类似于Encoder
的宏实现的.
implicit def applyMacro[T
为什么反射不能解决隐性证据?
avro4s
4.x 使用 Magnolia 但 avro4s
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)
}
}
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 反射时隐式解析失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!