2015-11-24 1 views
0

Пример случай класса: пытается создать общий конструктор запросовПолучить имя_поля и значение от случая объекта класса в Скале, во время выполнения

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 Reflection:

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. 

Попытка:

I am actually trying to retrieve the list of field and their values during runtime. 

One of the use cases, 

val list: Seq[Somehow(Field+Value)] = listingFieldWithValue(obj: UserQuery) 
for(o <- list){ 
o.field.type match{ 
case typeOne: FooType =>{ 
//Do something 
} 
case typeTwo: FooBarType =>{ 
//Do something else 
} 
} 

} 

ответ

2

Не совсем scala-reflect решение, но shapeless один. 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) 

Теперь

classAccessors(User(100, "Miles Sabin", Contact("+1 234 567 890", "Earth, TypeLevel Inc., 10"))) 

приведет к

Map(
'id -> PlainField(100, NumericField([email protected])), 
'name -> PlainField("Miles Sabin", StringField), 
'contact -> EmbeddedType(Map(
     'phone -> PlainField("+1 234 567 890", StringField), 
     'address -> PlainField("Earth, TypeLevel Inc., 10", StringField)))) 
+0

не такой подход будет аналогичен второму решение, которое я отправил, где я нужно было бы сопоставить «Значение» [Символ, Значение-> Любой] с определенным классом? – mane

+0

@mane На самом деле я приспособил его к вашему второму делу. Какой тип результата вам нужен? Поскольку бесформенный имеет полную информацию о типе на уровне компиляции, он может быть адаптирован ко всему. – Odomontois

+0

Также, как вы видите, он содержит встроенный объект «Карта», поэтому он выполняет полное рекурсивное преобразование. – Odomontois

Смежные вопросы