在运行时从scala中的case类对象检索字段名和值

在运行时从scala中的case类对象检索字段名和值,scala,scala-reflect,Scala,Scala Reflect,示例案例类:正在尝试创建通用查询生成器 case class BaseQuery(operand: String, value: String) case class ContactQuery(phone: BaseQuery, address: BaseQuery) case class UserQuery(id: BaseQuery, name: BaseQuery, contact: ContactQuery) val user = UserQuery(BaseQuery("equal"

示例案例类:正在尝试创建通用查询生成器

case class BaseQuery(operand: String, value: String)
case class ContactQuery(phone: BaseQuery, address: BaseQuery)
case class UserQuery(id: BaseQuery, name: BaseQuery, contact: ContactQuery)

val user = UserQuery(BaseQuery("equal","1"), BaseQuery("like","Foo"), ContactQuery(BaseQuery("eq","007-0000"),BaseQuery("like", "Foo City")))


//case class Contact(phone: String, address: String)

//case class User(id: Long, name: String, contact: Contact)

//val user = User(1, "Foo Dev", Contact("007-0000","Foo City"))
我们如何在scala中检索字段名和相应的值

进一步研究,

使用Scala反射的解决方案:

def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor => m
}.toList

// The above snippet returns the field names, and as input we have to 
//feed the 'TypeTag' of the case class. And since we are only feeding a   
//TypeTag we will not have access to any object values. Is there any Scala
// Reflection variant of the solution.
另一个解决方案

def getMapFromCaseClass(cc: AnyRef) =
(scala.collection.mutable.Map[String, Any]() /: cc.getClass.getDeclaredFields)
{
    (a, f) =>
        f.setAccessible(true)
        a + (f.getName -> f.get(cc))
}

// The above snippet returns a Map[String, Any] if we feed the case class 
//object. And we will need to match the map value to any of our other case 
//classes. If the class structure were simple, the above solution would be 
//helpful, but in case of complex case class this would not be the most efficient solution.
试图:

不是真正的scala reflect解决方案,而是一个。Shapeless是建立在隐式宏之上的,所以如果在编译时有类型信息,它比任何反射都要快

为了实现更新的需求,您需要一些时间来携带所需的类型信息

sealed trait TypeInfo[T]{
  //include here thing you like to handle at runtime
}
接下来,您可以为不同类型定义此类信息,并提供隐式解析。在我的示例中,这种实现也适用于以后的匹配

implicit case object StringField extends TypeInfo[String]

case class NumericField[N](implicit val num: Numeric[N]) extends TypeInfo[N]
implicit def numericField[N](implicit num: Numeric[N]): TypeInfo[N] = new NumericField[N]

case class ListField[E]() extends TypeInfo[List[E]]
implicit def listField[E] = new ListField[E]
然后定义了更适合模式匹配的结果结构

sealed trait FieldDef
case class PlainField[T](value: Any, info: TypeInfo[T]) extends FieldDef
case class EmbeddedType(values: Map[Symbol, FieldDef]) extends FieldDef
因此,任何结果都将是包含所需类型信息的值容器或用于更深入查看的容器

最后,我们可以定义概念实现

import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{ToTraversable, Mapper}

sealed abstract class ClassAccessors[C] {
  def apply(c: C): Map[Symbol, FieldDef]
}

trait LowPriorClassInfo extends Poly1 {
  implicit def fieldInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], info: TypeInfo[V]) =
    at[FieldType[K, V]](f => (witness.value: Symbol, PlainField(f, info)))
}

object classInfo extends LowPriorClassInfo {
  implicit def recurseInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], acc: ClassAccessors[V]) =
    at[FieldType[K, V]](f => (witness.value: Symbol, EmbeddedType(acc(f))))
}

implicit def provideClassAccessors[C, L <: HList, ML <: HList]
(implicit lgen: LabelledGeneric.Aux[C, L],
 map: Mapper.Aux[classInfo.type, L, ML],
 toList: ToTraversable.Aux[ML, List, (Symbol, FieldDef)]) =
  new ClassAccessors[C] {
    def apply(c: C) = toList(map(lgen.to(c))).toMap
  }

def classAccessors[C](c: C)(implicit acc: ClassAccessors[C]) = acc(c)
将导致

 Map(
 'id -> PlainField(100, NumericField( scala.math.Numeric$LongIsIntegral$@...)),
 'name -> PlainField("Miles Sabin", StringField), 
 'contact -> EmbeddedType( Map(
      'phone -> PlainField( "+1 234 567 890", StringField), 
      'address -> PlainField("Earth, TypeLevel Inc., 10", StringField))))
不是真正的scala reflect解决方案,而是一个。Shapeless是建立在隐式宏之上的,所以如果在编译时有类型信息,它比任何反射都要快

为了实现更新的需求,您需要一些时间来携带所需的类型信息

sealed trait TypeInfo[T]{
  //include here thing you like to handle at runtime
}
接下来,您可以为不同类型定义此类信息,并提供隐式解析。在我的示例中,这种实现也适用于以后的匹配

implicit case object StringField extends TypeInfo[String]

case class NumericField[N](implicit val num: Numeric[N]) extends TypeInfo[N]
implicit def numericField[N](implicit num: Numeric[N]): TypeInfo[N] = new NumericField[N]

case class ListField[E]() extends TypeInfo[List[E]]
implicit def listField[E] = new ListField[E]
然后定义了更适合模式匹配的结果结构

sealed trait FieldDef
case class PlainField[T](value: Any, info: TypeInfo[T]) extends FieldDef
case class EmbeddedType(values: Map[Symbol, FieldDef]) extends FieldDef
因此,任何结果都将是包含所需类型信息的值容器或用于更深入查看的容器

最后,我们可以定义概念实现

import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{ToTraversable, Mapper}

sealed abstract class ClassAccessors[C] {
  def apply(c: C): Map[Symbol, FieldDef]
}

trait LowPriorClassInfo extends Poly1 {
  implicit def fieldInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], info: TypeInfo[V]) =
    at[FieldType[K, V]](f => (witness.value: Symbol, PlainField(f, info)))
}

object classInfo extends LowPriorClassInfo {
  implicit def recurseInfo[K <: Symbol, V](implicit witness: Witness.Aux[K], acc: ClassAccessors[V]) =
    at[FieldType[K, V]](f => (witness.value: Symbol, EmbeddedType(acc(f))))
}

implicit def provideClassAccessors[C, L <: HList, ML <: HList]
(implicit lgen: LabelledGeneric.Aux[C, L],
 map: Mapper.Aux[classInfo.type, L, ML],
 toList: ToTraversable.Aux[ML, List, (Symbol, FieldDef)]) =
  new ClassAccessors[C] {
    def apply(c: C) = toList(map(lgen.to(c))).toMap
  }

def classAccessors[C](c: C)(implicit acc: ClassAccessors[C]) = acc(c)
将导致

 Map(
 'id -> PlainField(100, NumericField( scala.math.Numeric$LongIsIntegral$@...)),
 'name -> PlainField("Miles Sabin", StringField), 
 'contact -> EmbeddedType( Map(
      'phone -> PlainField( "+1 234 567 890", StringField), 
      'address -> PlainField("Earth, TypeLevel Inc., 10", StringField))))

这种方法是否与我发布的第二个解决方案类似,我需要将“Value”[Symbol,Value->Any]与某个类匹配?@mane事实上,我已经将它改编为您的第二个案例。你需要什么样的结果类型?由于Shapess在编译级别具有完整的类型信息,因此它可以适应任何情况。正如您所看到的,它包含嵌入式映射对象,因此它正在进行完全的递归转换。@odmontois我添加了一个用例,我尝试尽可能地进行描述,希望能有所帮助。更新了问题。我只是想省略一个流程层,在这个层中,我需要将“Any”与我所拥有的案例类列表进行比较。这种方法是否与我发布的第二个解决方案类似,我需要将“Value”[Symbol,Value->Any]与某个类相匹配?@mane实际上,我已经将它改编为您的第二个案例。你需要什么样的结果类型?由于Shapess在编译级别具有完整的类型信息,因此它可以适应任何情况。正如您所看到的,它包含嵌入式映射对象,因此它正在进行完全的递归转换。@odmontois我添加了一个用例,我尝试尽可能地进行描述,希望能有所帮助。更新了问题。我只是想省略一个流程层,在这里我需要将“Any”与我拥有的案例类列表进行比较。