我在案例类中有一个嵌套的数据结构,比如
Update2 所有的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类中的所有字段,以及它所有的子类。
this回答的代码解决了问题的第一步。它列出了第一级的所有字段(A类)。我尝试将其更改为递归调用相同的方法,但我无法获取MethodSymbol的TypeTag信息。
我期待的结果是一个接收A作为参数的方法,并返回
(b.id,b.name,c.cNode,c.cUser,d.dData,d.dFile)
如何从案例类中获取子字段属性名称?
更新
我正在使用scala 2.11
我还希望它由反射/宏生成,因为数据结构很复杂,我希望在更新案例类时更新它。
答案 0 :(得分:4)
您可以致电methodSymbol.returnType
。它将为您提供案例访问器的返回类型,然后您可以递归地收集它的所有案例访问器。
以下是一个完整的示例(假设每个字段都是Option
):
scala> :paste
// Entering paste mode (ctrl-D to finish)
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)
import scala.reflect.runtime.universe._
def allFields[T: TypeTag]: List[String] = {
def rec(tpe: Type): List[List[Name]] = {
val collected = tpe.members.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
if (collected.nonEmpty)
collected.flatMap(m => rec(m.returnType.typeArgs.head).map(m.name :: _))
else
List(Nil)
}
rec(typeOf[T]).map(_.mkString("."))
}
// Exiting paste mode, now interpreting.
scala> allFields[A]
res0: List[String] = List(d.dField, d.dData, c.cuser, c.cNode, b.name, b.id)
答案 1 :(得分:0)
重要提示
在我看来,这个答案并不是一个好的答案(我是根据OP的要求发布的)。它涉及来自shapeless
库的复杂结构,以避免处理宏或反射(实际上,它使用了引擎盖下的宏,但shapeless
允许忘记它们。)
这是基于shapeless
Generic
个宏。它涉及为您的数据类型创建一个类型类,并为任何具有LabelledGeneric
无形状的类型推断此类型类的实例,即具有sealed trait
和case class
的数据结构:
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)
}
}
请注意,在您只处理case class
es时,不需要联合产品部分(您可以将LabelledTypeClass
替换为LabelledProductTypeClass
,将Companion
替换为Option
。但是,由于implicitly[RecursiveFields[A]].fields
是副产品,在我们的案例中并非如此,但我不清楚在这种情况下我们应该做出什么选择(我选择在副产品中采取第一种可能的选择,但这并不令人满意)。
要使用此功能,只需致电.
即可获取其元素为所需字段的列表(在b.name
上拆分,以便List(b, name)
实际保留为<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<i id="xyz" class="class1" >hey</i>
<div id="abc" class="class2" style="display: block;">lo</div>
<script type="text/javascript>
$(function() {
$(".xyz").click(function() {
console.log("element with class xyz was clicked");
$(".abc").css('display': 'none');
});
});
</script>
) 。