Because each field has a different type, you would need one type parameter per field. This is because to write these fields, you need to provide (implicitly) the Writes
instances for the corresponding types (to method toJson
), and those are resolved statically.
One solution to work around this is to split the process in two parts: one method that you call for each field to extract the field accessor and pack it with the corresponding WriteS
instance (this can even be maed an implicit conversion from the paairs that you are already passing), and one method that takes the whole and creates the final WriteS
instance. Something like this (illustrative, untested):
class WriteSFieldAccessor[C,T] private ( val title: String, val accessor: C => Any )( implicit val writes: Writes[T] )
implicit def toWriteSFieldAccessor[C,T:Writes]( titleAndAccessor: (String, C => T) ): WriteSFieldAccessor = {
new WriteSFieldAccessor[C,T]( titleAndAccessor._1, titleAndAccessor._2 )
}
def makeSimpleWrites[C](fields: WriteSFieldAccessor[C,_]*) : Writes[C] = {
new Writes[C] {
def writes(c: C) : JsValue = {
val jsFields = fields map { f: WriteSFieldAccessor =>
val jsField = toJson[Any](f.accessor(c))(f.writes.asInstanceOf[Writes[Any]])
(f.title, jsField)
}
JsObject(jsFields)
}
}
}
// Each pair below is implicitly converted to a WriteSFieldAccessor instance, capturing the required information and passing it to makeSimpleWrites
implicit val fooWrites : Writes[Foo] = makeSimpleWrites[Foo]("title" -> {_.title}, "lines" -> {_.lines})
The interesting part is toJson[Any](f.accessor(c))(f.writes..asInstanceOf[Writes[Any]])
. You just pass Any
as a the static type but explicitly pass the (normally implicit) Writes
instance.