我试图基于类路径在反射中生成Avro4s的RecordFormat。下面的代码引发一个错误。
case class MyCaseClass(a: Int)
println(toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|RecordFormat[MyCaseClass]
|""".stripMargin
)
}())无法为com.sksamuel.avro4s.Decodermypackage.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] {
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)
}
}我可以看到,它可以解决Encoder[MyCaseClass]和SchemaFor[MyCaseClass],但是对于Decoder[MyCaseClass]失败。
相同的代码可以在不反射的情况下解析RecordFormat[MyCaseClass]。
我可以看到,Decoder是用类似于Encoder的宏实现的。
implicit def applyMacro[T <: Product]: Decoder[T] = macro applyMacroImpl[T]为什么反射不能解决隐含的证据?
发布于 2020-11-19 04:55:30
avro4s 4.x使用木兰,而avro4s 2.x使用原始隐式宏 + 无形。
通常,使用反射工具箱在运行时物化类型类的不应该有重大问题,即使类型类是用宏定义的。
现在的问题是定义com.sksamuel.avro4s.Decoder的宏有一个错误。线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)
}
}
}
"""
)指sys.error而不是卫生学 _root_.scala.sys.error。
如果您修复了这一行,Decoder[MyCaseClass]和RecordFormat[MyCaseClass]将在工具箱中工作。
println(toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|RecordFormat[MyCaseClass]
|""".stripMargin
)
}()) //com.sksamuel.avro4s.RecordFormat$$anon$1@25109d84因此,快速修复方法是删除行。
libraryDependencies += "com.sksamuel.avro4s" %% "avro4s-core" % "2........."在build.sbt中,添加
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
libraryDependencies += "org.apache.avro" % "avro" % "1.8.2"(否则您将拥有NoClassDefFoundError),并将以下修补的jars放入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如果创建如下所示,则始终可以调试使用工具箱生成的基于隐式或基于宏的代码
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"
)如果你这样做了
println(reify{
Decoder[MyCaseClass]
}.tree)印出来
Decoder.apply[MyCaseClass](Decoder.applyMacro)因此隐式Decoder[MyCaseClass]被解析为Decoder.applyMacro[MyCaseClass]。
带着原始的未修补的罐子
toolBox.compile {
toolBox.parse(
s"""
|import com.sksamuel.avro4s._
|import mypackage.MyCaseClass
|Decoder.applyMacro[MyCaseClass]
|""".stripMargin
)
}()抛出
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
object error is not a member of package syshttps://stackoverflow.com/questions/64903163
复制相似问题