Feyyaz による解決策は、可能な限り最良の解決策のようです。考えられるすべてのサブクラスのパターン マッチングを回避するために、親でコントラクトを定義しているため、すべてのサブクラスがエンコーダーを提供する必要があります。完璧かどうかはわかりませんが、期待どおりの動作をします。同様の解決策を探している人に役立つことを願っています。
import argonaut._, Argonaut._
import scala.collection.TraversableLike
trait HasEncoder[T <: HasEncoder[T]] { self: T =>
def encoder: EncodeJson[T]
lazy val json = encoder.encode(self)
}
object HasEncoder {
implicit def listToRichHasEncoderList[A <: HasEncoder[_], Repr](coll: TraversableLike[A, Repr]): RichHasEncoderList[A, Repr] = new RichHasEncoderList[A, Repr](coll)
}
class RichHasEncoderList[A <: HasEncoder[_], Repr](coll: TraversableLike[A, Repr]) {
lazy val json = jArray(coll.toList.map(_.json))
}
上記のソリューションでは、アルゴナウトはリストが json 配列 (jArray) を構築することを期待しているため、トラバース可能なものはすべて受け入れてリストに変換する必要がありました。その部分を改善できるかどうかはわかりません。
そしてテストケース(暗黙の「json」値がリストで利用できるように、上記のコードが存在するパッケージを必ずインポートしてください):
import argonaut._, Argonaut._
import org.scalatest.{Matchers, FlatSpec}
/**
* Created by jamesanto on 12/17/15.
*/
class HasEncoderTest extends FlatSpec with Matchers {
case class MissingParameter(name: String) extends HasEncoder[MissingParameter] {
override def encoder: EncodeJson[MissingParameter] = casecodec1(MissingParameter.apply, MissingParameter.unapply)("name")
}
case class InvalidParameter(name: String, expected: String, actual: String) extends HasEncoder[InvalidParameter] {
override def encoder: EncodeJson[InvalidParameter] = casecodec3(InvalidParameter.apply, InvalidParameter.unapply)("name", "expected", "actual")
}
it should "encode list of objects of classes that extend HasEncoder" in {
val list = List(MissingParameter("email"), InvalidParameter("dob", "DOB in yyyy/MM/dd format", "10/10/1985"))
list.json.nospaces should be ("""[{"name":"email"},{"name":"dob","expected":"DOB in yyyy/MM/dd format","actual":"10/10/1985"}]""")
}
}