例如:
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 :(得分:1)
你可以使用shapeless:
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
看一下无形的伟大指南