2017-03-15 61 views
1

例如:生成所有可能的點號查詢一個模型

import play.api.libs.json.Json 

case class UserDetails(
         username: String, 
         contact: Contact 
        ) 


object UserDetails { 

    implicit val userDetailsFormatter = Json.format[UserDetails] 

} 


case class Contact(phone: String, email: String) 


object Contact { 

    implicit val contactFormatter = Json.format[Contact] 

} 

通過上述UserDetails模型映射到文檔中的MongoDB集合,如果我想手動列出以點表示所有可能出現的問題,那麼他們在這裏:

username 
contact.phone 
contact.email 

我該如何以編程方式生成這些?

+0

於無形隱式的宏和無形的類型類可以很容易的工作。使用'shapeless.LabelledGeneric'編碼爲HList,使用標籤中的'Keys'來獲得你想要的。 – flavian

回答

1

您可以使用無形此:

case class UserDetails(username: String, contact: Contact) 
case class Contact(phone: String, email: Email) 
case class Email(name: String, domain: String) 

// a class to store field name and subfields info 
case class FieldInfo(name: String, subfields: List[FieldInfo]) 

// our main typeclass 
// we will use shapeless to derive instance for UserDetails above 
trait FieldList[A] { 
    def fields: List[FieldInfo] 
} 

object FieldList { 
    import shapeless.{ LabelledGeneric, HList, ::, HNil, Witness, Lazy } 
    import shapeless.labelled.{ FieldType, field } 

    // helper function for creating typeclass instance 
    def createInstance[A](f: => List[FieldInfo]): FieldList[A] = new FieldList[A] { 
    def fields = f 
    } 

    // instance for String, here we just care for field name so just a dummy instance... 
    // you should add instance for other basic types (Int, Double...) if required 
    implicit val stringInstance: FieldList[String] = createInstance(Nil) 

    // define instance for base case (HNil) 
    implicit val hnilInstance: FieldList[HNil] = createInstance(Nil) 

    // define rule for HList 
    implicit def hlistInstance[K <: Symbol, H, T <: HList](
    implicit witness: Witness.Aux[K], 
    hInstance: Lazy[FieldList[H]], 
    tInstance: FieldList[T]): FieldList[FieldType[K, H] :: T] = createInstance { 
    val headFieldName: String = witness.value.name 
    val headSubFields = hInstance.value.fields 
    val tail = tInstance.fields 
    FieldInfo(headFieldName, headSubFields) :: tail 
    } 

    // rule for generic ADT 
    implicit def genericObjectInstance[A, H <: HList](
    implicit generic: LabelledGeneric.Aux[A, H], 
    hlistInstance: Lazy[FieldList[H]]): FieldList[A] = createInstance { hlistInstance.value.fields } 

    // some utils... 
    def show(field: FieldInfo): List[String] = { 
    if (field.subfields.isEmpty) List(field.name) 
    else field.subfields.flatMap(sub => show(sub).map(field.name + "." + _)) 
    } 

    def getFieldsName[A](implicit instance: FieldList[A]) = { 
    val fields = instance.fields 
    fields.flatMap(show).mkString("\n") 
    } 
} 

// ok, test it! 
object ShapelessLabelled extends App { 
    println(FieldList.getFieldsName[UserDetails]) 
} 

爲了更好地理解這一點,你可以看看偉大嚮導在https://github.com/underscoreio/shapeless-guide

相關問題