如何获取Scala案例类的字段和子字段列表

如何获取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类中有一个嵌套的数据结构,比如

更新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 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)

您可能可以这样做