如何获取Scala案例类的字段和子字段列表
我在case类中有一个嵌套的数据结构,比如 更新2所有VAL都是可选的如何获取Scala案例类的字段和子字段列表,scala,reflection,case-class,Scala,Reflection,Case Class,我在case类中有一个嵌套的数据结构,比如 更新2所有VAL都是可选的 case class A(b:Option[B] = None,c:Option[C] = None,d:Option[D] = None) case class B(id:Option[String] = None, name:Option[String] = None) case class C(cNode:Option[String] = None, cuser:Option[String] = None) case
case class A(b:Option[B] = None,c:Option[C] = None,d:Option[D] = None)
case class B(id:Option[String] = None, name:Option[String] = None)
case class C(cNode:Option[String] = None, cuser:Option[String] = None)
case class D(dData:Option[String] = None, dField:Option[String] = None)
我正在寻找一个正则表达式来跟踪从类a到所有子类的所有字段
答案中的代码解决了我问题的第一步。它列出了第一级(A类)的所有字段。我尝试将其更改为递归调用相同的方法,但无法获取MethodSymbol的TypeTag信息
我期望的结果是一个方法,它接收as参数并返回
(b.id,b.name,c.cNode,c.cUser,d.dData,d.dFile)
如何从案例类中获取子字段属性名称
更新
我正在使用scala 2.11
我还希望它由反射/宏生成,因为数据结构很复杂,我希望它在更新case类时更新。您可以调用
methodSymbol.returnType
。它将为您提供案例访问器的返回类型,然后您可以递归地收集其所有案例访问器
下面是一个完整的示例(假设每个字段都是一个选项
):
您可以调用
methodSymbol.returnType
。它将为您提供案例访问器的返回类型,然后您可以递归地收集其所有案例访问器
下面是一个完整的示例(假设每个字段都是一个选项
):
重要提示 这个答案在我看来不是一个好答案(我是应OP的要求发布的)。它涉及
shapeless
库中的复杂结构,以避免处理宏或反射(实际上,它在引擎盖下使用宏,但shapeless
允许忘记它们)
这是基于
shapeless
Generic
宏的。它涉及为您的数据类型创建一个typeclass,并为任何类型推断此typeclass的实例,该类型具有无形状的LabelledEnergic
,即具有密封特征
s和案例类
es的数据结构:
import shapeless.{:+:, CNil, Coproduct, HList, HNil, LabelledTypeClass, LabelledTypeClassCompanion}
trait RecursiveFields[T] {
def fields: List[List[String]]
override def toString = fields.map(_.mkString(".")).mkString("(", ", ", ")")
}
object RecursiveFields extends LabelledTypeClassCompanion[RecursiveFields] {
def apply[T](f: List[List[String]]): RecursiveFields[T] = new RecursiveFields[T] {
val fields = f
}
implicit val string: RecursiveFields[String] = apply[String](Nil)
implicit def anyVal[T <: AnyVal]: RecursiveFields[T] = apply[T](Nil)
object typeClass extends LabelledTypeClass[RecursiveFields] {
override def product[H, T <: HList](name: String, ch: RecursiveFields[H], ct: RecursiveFields[T]): RecursiveFields[shapeless.::[H, T]] =
RecursiveFields{
val hFields = if (ch.fields.isEmpty) List(List(name)) else ch.fields.map(name :: _)
hFields ++ ct.fields
}
override def emptyProduct: RecursiveFields[HNil] = RecursiveFields(Nil)
override def project[F, G](instance: => RecursiveFields[G], to: (F) => G, from: (G) => F): RecursiveFields[F] =
RecursiveFields(instance.fields)
override def coproduct[L, R <: Coproduct](name: String, cl: => RecursiveFields[L], cr: => RecursiveFields[R]): RecursiveFields[:+:[L, R]] =
RecursiveFields[L :+: R](product(name, cl, emptyProduct).fields)
override def emptyCoproduct: RecursiveFields[CNil] = RecursiveFields(Nil)
}
}
import shapeless.{:+:,CNil,copproduct,HList,HNil,LabelledTypeClass,LabelledTypeClassCompanion}
特征递归字段[T]{
定义字段:列表[列表[字符串]]
重写def toString=fields.map(u.mkString(“.”)。mkString((“,”、“,”)
}
对象RecursiveFields扩展LabelledTypeClassCompanion[RecursiveFields]{
def apply[T](f:List[List[String]]):RecursiveFields[T]=新的RecursiveFields[T]{
val字段=f
}
隐式val字符串:RecursiveFields[string]=apply[string](Nil)
隐式def anyVal[tg,from:(G)=>F):递归字段[F]=
递归字段(instance.fields)
重写def coproduct[L,R RecursiveFields[L],cr:=>RecursiveFields[R]):RecursiveFields[:+:[L,R]]=
递归字段[L:+:R](产品(名称、cl、emptyProduct).fields)
重写def emptyCoproduct:RecursiveFields[CNil]=RecursiveFields(Nil)
}
}
请注意,当您仅处理案例类时,不需要副产品部分(您可以将LabelledTypeClass
替换为LabelledProductTypeClass
,对于Companion
,也可以将其替换为LabelledTypeClass
)但是,由于选项
是一个副产品,在我们的案例中并非如此,但不清楚在这种情况下我们应该做出什么选择(我选择了副产品中的第一个可能选择,但这并不令人满意)
要使用它,只需隐式调用[RecursiveFields[A]].fields
即可获得一个列表,其中的元素是所需的字段(在
上拆分),这样b.name
实际上被保存为list(b,name)
。重要提示
在我看来,这个答案不是一个好答案(我在OP的要求下发布了它)。它涉及无形状
库中的复杂结构,以避免处理宏或反射(实际上,它在引擎盖下使用宏,但无形状
允许忘记它们)
这基于shapeledgeneric
Generic
宏。它涉及为您的数据类型创建一个typeclass,并为任何具有LabelledGeneric
的Shapeledgeneric的类型推断此typeclass的实例,即具有sealed trait
s和case class
es的数据结构:
import shapeless.{:+:, CNil, Coproduct, HList, HNil, LabelledTypeClass, LabelledTypeClassCompanion}
trait RecursiveFields[T] {
def fields: List[List[String]]
override def toString = fields.map(_.mkString(".")).mkString("(", ", ", ")")
}
object RecursiveFields extends LabelledTypeClassCompanion[RecursiveFields] {
def apply[T](f: List[List[String]]): RecursiveFields[T] = new RecursiveFields[T] {
val fields = f
}
implicit val string: RecursiveFields[String] = apply[String](Nil)
implicit def anyVal[T <: AnyVal]: RecursiveFields[T] = apply[T](Nil)
object typeClass extends LabelledTypeClass[RecursiveFields] {
override def product[H, T <: HList](name: String, ch: RecursiveFields[H], ct: RecursiveFields[T]): RecursiveFields[shapeless.::[H, T]] =
RecursiveFields{
val hFields = if (ch.fields.isEmpty) List(List(name)) else ch.fields.map(name :: _)
hFields ++ ct.fields
}
override def emptyProduct: RecursiveFields[HNil] = RecursiveFields(Nil)
override def project[F, G](instance: => RecursiveFields[G], to: (F) => G, from: (G) => F): RecursiveFields[F] =
RecursiveFields(instance.fields)
override def coproduct[L, R <: Coproduct](name: String, cl: => RecursiveFields[L], cr: => RecursiveFields[R]): RecursiveFields[:+:[L, R]] =
RecursiveFields[L :+: R](product(name, cl, emptyProduct).fields)
override def emptyCoproduct: RecursiveFields[CNil] = RecursiveFields(Nil)
}
}
import shapeless.{:+:,CNil,copproduct,HList,HNil,LabelledTypeClass,LabelledTypeClassCompanion}
特征递归字段[T]{
定义字段:列表[列表[字符串]]
重写def toString=fields.map(uu.mkString(“.”).mkString(“(“,”,“,”,”)”)
}
对象RecursiveFields扩展LabelledTypeClassCompanion[RecursiveFields]{
def apply[T](f:List[List[String]]):RecursiveFields[T]=新的RecursiveFields[T]{
val字段=f
}
隐式val字符串:RecursiveFields[string]=apply[string](Nil)
隐式def anyVal[tg,from:(G)=>F):递归字段[F]=
递归字段(instance.fields)
重写def coproduct[L,R RecursiveFields[L],cr:=>RecursiveFields[R]):RecursiveFields[:+:[L,R]]=
递归字段[L:+:R](产品(名称、cl、emptyProduct).fields)
重写def emptyCoproduct:RecursiveFields[CNil]=RecursiveFields(Nil)
}
}
请注意,当您仅处理案例类时,不需要副产品部分(您可以将LabelledTypeClass
替换为LabelledProductTypeClass
,对于Companion
,也可以将其替换为LabelledTypeClass
)但是,由于选项
是一个副产品,在我们的案例中并非如此,但不清楚在这种情况下我们应该做出什么选择(我选择了副产品中的第一个可能选择,但这并不令人满意)
要使用它,只需隐式调用[RecursiveFields[A]]].fields
就可以得到一个列表,其中的元素是所需的字段(在
上拆分),这样b.name
实际上被保存为list(b,name)
。您可能可以这样做