case class 到 shapeless 通用转换
case class to shapeless Generic conversion
问题:
我有一个案例 class Foo
可以是任何东西。
我需要一个函数来创建密码查询。
这个新函数的签名应该是
def createQueryString[T](t: T): String = ???
示例:
我有Foo
假设有两个成员
case class Foo(x: Int, y: String)
我需要将其转换为密码
CREATE (f:Foo { x: "1", y: "Hello" }) RETURN f
如果我将Foo(1, "Hello")
传递给上面提到的函数createQueryString
createQueryString[Foo](Foo(1, "Hello"))
到目前为止我尝试了什么?
我已经尝试使用 shapeless 的 Generic
和 Aux
来实现这个
import shapeless._
case class Foo(x: Int, y: String)
def foo[T, HL <: HList](instance: T)(
implicit gen: Generic.Aux[T, HL]
): HL = {
gen.to(instance)
}
val myFoo = foo(Foo(1, "Hello"))
s"""CREATE (f:Foo { x: "${myFoo(0)}", y: "${myFoo(1)}" }) RETURN f"""
有什么办法可以用这个foo(Foo(1, "Hello"))
在上面提到的createQueryString
里面实现吗?我想基本上将类型传递给这个函数。有点像
def createQueryString[T](t: T): String = {
val gen = foo(t) // to get the generic
s"""CREATE (t: T { x: "${gen(0)}", y: "${gen(1)}" }) RETURN t"""
}
像这样。但是通过这样做我得到以下错误
Error:(77, 22) could not find implicit value for parameter gen: shapeless.Generic.Aux[T,HL]
val gen = foo(t) // to get the generic
问题:
- 我在上次使用
Aux
的实现中遗漏了什么?
- 我怎样才能改进它使其足够通用以作为参数合并到密码查询中?在 class.
的情况下,可以有多个 x
和 y
您忘记了隐式参数(或上下文边界)。
Generic
不仅可以产生HList
,还可以产生Coproduct
。这就是为什么如果你松散绑定 <: HList
编译器不知道如何将 Generic
的 Repr
应用到 0
、1
.
尝试
import shapeless.ops.hlist.At
import shapeless.nat._
def foo[T](instance: T)(
implicit gen: Generic[T]
): gen.Repr = {
gen.to(instance)
}
def createQueryString[T, L <: HList](t: T)(implicit
g: Generic.Aux[T, L],
at0: At[L, _0],
at1: At[L, _1]
): String = {
val gen = foo(t) // to get the generic
s"""CREATE (t: T { x: "${gen(0)}", y: "${gen(1)}" }) RETURN t"""
}
如果您需要标签 x
、y
,那么您需要 LabelledGeneric
而不是 Generic
。
如果您有任意数量的参数,那么您可以将 HList 转换为所需的形式,然后将其折叠为字符串。
{ x: "1", y: "Hello" }
看起来像 JSON。看看Circe.
这可能不是您正在寻找的答案,但对于您给出的示例,使用 Reflection
:
def createQueryString[T](t: T): String = {
def formatFields() = {
t.getClass().getDeclaredFields().filterNot(_.getName.startsWith("(")).map(f => {
f.setAccessible(true)
f.getName + ": \"" + f.get(t).toString + "\""
}).mkString(", ")
}
val className = t.getClass.getSimpleName
val paramName = nameOf(t)
s"""CREATE ($paramName:$className { $formatFields }) RETURN $paramName"""
}
输出:
CREATE (t:Foo { x: "1", y: "Hello" }) RETURN t
"(" 过滤掉构造函数,只给出 vars/vals。
可以定制此代码以通过递归管理嵌套 类。
依赖关系:
反射支持:
"org.scala-lang" % "scala-reflect" % scalaVersion.value
为了方便获取参数名:
"com.github.dwickern" %% "scala-nameof" % "4.0.0" % "provided"
附带说明一下,这可能是使用反射的罕见好用例之一,因为任何硬编码值都不会改变或访问任何内容。
问题:
我有一个案例 class Foo
可以是任何东西。
我需要一个函数来创建密码查询。
这个新函数的签名应该是
def createQueryString[T](t: T): String = ???
示例:
我有Foo
假设有两个成员
case class Foo(x: Int, y: String)
我需要将其转换为密码
CREATE (f:Foo { x: "1", y: "Hello" }) RETURN f
如果我将Foo(1, "Hello")
传递给上面提到的函数createQueryString
createQueryString[Foo](Foo(1, "Hello"))
到目前为止我尝试了什么?
我已经尝试使用 shapeless 的 Generic
和 Aux
来实现这个
import shapeless._
case class Foo(x: Int, y: String)
def foo[T, HL <: HList](instance: T)(
implicit gen: Generic.Aux[T, HL]
): HL = {
gen.to(instance)
}
val myFoo = foo(Foo(1, "Hello"))
s"""CREATE (f:Foo { x: "${myFoo(0)}", y: "${myFoo(1)}" }) RETURN f"""
有什么办法可以用这个foo(Foo(1, "Hello"))
在上面提到的createQueryString
里面实现吗?我想基本上将类型传递给这个函数。有点像
def createQueryString[T](t: T): String = {
val gen = foo(t) // to get the generic
s"""CREATE (t: T { x: "${gen(0)}", y: "${gen(1)}" }) RETURN t"""
}
像这样。但是通过这样做我得到以下错误
Error:(77, 22) could not find implicit value for parameter gen: shapeless.Generic.Aux[T,HL]
val gen = foo(t) // to get the generic
问题:
- 我在上次使用
Aux
的实现中遗漏了什么? - 我怎样才能改进它使其足够通用以作为参数合并到密码查询中?在 class. 的情况下,可以有多个
x
和 y
您忘记了隐式参数(或上下文边界)。
Generic
不仅可以产生HList
,还可以产生Coproduct
。这就是为什么如果你松散绑定 <: HList
编译器不知道如何将 Generic
的 Repr
应用到 0
、1
.
尝试
import shapeless.ops.hlist.At
import shapeless.nat._
def foo[T](instance: T)(
implicit gen: Generic[T]
): gen.Repr = {
gen.to(instance)
}
def createQueryString[T, L <: HList](t: T)(implicit
g: Generic.Aux[T, L],
at0: At[L, _0],
at1: At[L, _1]
): String = {
val gen = foo(t) // to get the generic
s"""CREATE (t: T { x: "${gen(0)}", y: "${gen(1)}" }) RETURN t"""
}
如果您需要标签 x
、y
,那么您需要 LabelledGeneric
而不是 Generic
。
如果您有任意数量的参数,那么您可以将 HList 转换为所需的形式,然后将其折叠为字符串。
{ x: "1", y: "Hello" }
看起来像 JSON。看看Circe.
这可能不是您正在寻找的答案,但对于您给出的示例,使用 Reflection
:
def createQueryString[T](t: T): String = {
def formatFields() = {
t.getClass().getDeclaredFields().filterNot(_.getName.startsWith("(")).map(f => {
f.setAccessible(true)
f.getName + ": \"" + f.get(t).toString + "\""
}).mkString(", ")
}
val className = t.getClass.getSimpleName
val paramName = nameOf(t)
s"""CREATE ($paramName:$className { $formatFields }) RETURN $paramName"""
}
输出:
CREATE (t:Foo { x: "1", y: "Hello" }) RETURN t
"(" 过滤掉构造函数,只给出 vars/vals。 可以定制此代码以通过递归管理嵌套 类。
依赖关系:
反射支持:
"org.scala-lang" % "scala-reflect" % scalaVersion.value
为了方便获取参数名:
"com.github.dwickern" %% "scala-nameof" % "4.0.0" % "provided"
附带说明一下,这可能是使用反射的罕见好用例之一,因为任何硬编码值都不会改变或访问任何内容。