为扩展特征的 class 获取 Play JSON JsValueWrapper
Getting a Play JSON JsValueWrapper for a class that extends a trait
我正在为速度生成JSON,其中单位可能会有所不同。我有一个 SpeedUnit 特征和扩展它的 classes(Knots、MetersPerSecond、MilesPerHour)。 JSON Play documentation 说 "To convert your own models to JsValues, you must define implicit Writes converters and provide them in scope." 我在大多数地方都可以使用它,但当我有 class 扩展特征时却不行。我究竟做错了什么?或者是否有我可以或应该使用的 Enum 变体?
// Type mismatch: found (String, SpeedUnit), required (String, Json.JsValueWrapper)
// at 4th line from bottom: "speedunit" -> unit
import play.api.libs.json._
trait SpeedUnit {
// I added this to SpeedUnit thinking it might help, but it didn't.
implicit val speedUnitWrites = new Writes[SpeedUnit] {
def writes(x: SpeedUnit) = Json.toJson("UnspecifiedSpeedUnit")
}
}
class Knots extends SpeedUnit {
implicit val knotsWrites = new Writes[Knots] {
def writes(x: Knots) = Json.toJson("KT")
}
}
class MetersPerSecond extends SpeedUnit {
implicit val metersPerSecondWrites = new Writes[MetersPerSecond] {
def writes(x: MetersPerSecond) = Json.toJson("MPS")
}
}
class MilesPerHour extends SpeedUnit {
implicit val milesPerHourWrites = new Writes[MilesPerHour] {
def writes(x: MilesPerHour) = Json.toJson("MPH")
}
}
// ...
class Speed(val value: Int, val unit: SpeedUnit) {
implicit val speedWrites = new Writes[Speed] {
def writes(x: Speed) = Json.obj(
"value" -> value,
"speedUnit" -> unit // THIS LINE DOES NOT TYPE-CHECK
)
}
}
Writes
是类型 class 的示例,这意味着对于给定的 A
,您需要 Writes[A]
的单个实例,而不是每个 A
实例。如果您来自 Java 背景,请考虑 Comparator
而不是 Comparable
。
import play.api.libs.json._
sealed trait SpeedUnit
case object Knots extends SpeedUnit
case object MetersPerSecond extends SpeedUnit
case object MilesPerHour extends SpeedUnit
object SpeedUnit {
implicit val speedUnitWrites: Writes[SpeedUnit] = new Writes[SpeedUnit] {
def writes(x: SpeedUnit) = Json.toJson(
x match {
case Knots => "KTS"
case MetersPerSecond => "MPS"
case MilesPerHour => "MPH"
}
)
}
}
case class Speed(value: Int, unit: SpeedUnit)
object Speed {
implicit val speedWrites: Writes[Speed] = new Writes[Speed] {
def writes(x: Speed) = Json.obj(
"value" -> x.value,
"speedUnit" -> x.unit
)
}
}
然后:
scala> Json.toJson(Speed(10, MilesPerHour))
res0: play.api.libs.json.JsValue = {"value":10,"speedUnit":"MPH"}
我已将 Writes
实例放在这两种类型的伴随对象中,但它们可以放在其他地方(例如,如果您不想在模型中混淆序列化问题)。
您还可以使用 Play JSON 的函数 API:
来简化(或至少简洁化)这一点
sealed trait SpeedUnit
case object Knots extends SpeedUnit
case object MetersPerSecond extends SpeedUnit
case object MilesPerHour extends SpeedUnit
case class Speed(value: Int, unit: SpeedUnit)
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val speedWrites: Writes[Speed] = (
(__ \ 'value).write[Int] and
(__ \ 'speedUnit).write[String].contramap[SpeedUnit] {
case Knots => "KTS"
case MetersPerSecond => "MPS"
case MilesPerHour => "MPH"
}
)(unlift(Speed.unapply))
您采用哪种方法(功能性或显性)很大程度上取决于品味。
我正在为速度生成JSON,其中单位可能会有所不同。我有一个 SpeedUnit 特征和扩展它的 classes(Knots、MetersPerSecond、MilesPerHour)。 JSON Play documentation 说 "To convert your own models to JsValues, you must define implicit Writes converters and provide them in scope." 我在大多数地方都可以使用它,但当我有 class 扩展特征时却不行。我究竟做错了什么?或者是否有我可以或应该使用的 Enum 变体?
// Type mismatch: found (String, SpeedUnit), required (String, Json.JsValueWrapper)
// at 4th line from bottom: "speedunit" -> unit
import play.api.libs.json._
trait SpeedUnit {
// I added this to SpeedUnit thinking it might help, but it didn't.
implicit val speedUnitWrites = new Writes[SpeedUnit] {
def writes(x: SpeedUnit) = Json.toJson("UnspecifiedSpeedUnit")
}
}
class Knots extends SpeedUnit {
implicit val knotsWrites = new Writes[Knots] {
def writes(x: Knots) = Json.toJson("KT")
}
}
class MetersPerSecond extends SpeedUnit {
implicit val metersPerSecondWrites = new Writes[MetersPerSecond] {
def writes(x: MetersPerSecond) = Json.toJson("MPS")
}
}
class MilesPerHour extends SpeedUnit {
implicit val milesPerHourWrites = new Writes[MilesPerHour] {
def writes(x: MilesPerHour) = Json.toJson("MPH")
}
}
// ...
class Speed(val value: Int, val unit: SpeedUnit) {
implicit val speedWrites = new Writes[Speed] {
def writes(x: Speed) = Json.obj(
"value" -> value,
"speedUnit" -> unit // THIS LINE DOES NOT TYPE-CHECK
)
}
}
Writes
是类型 class 的示例,这意味着对于给定的 A
,您需要 Writes[A]
的单个实例,而不是每个 A
实例。如果您来自 Java 背景,请考虑 Comparator
而不是 Comparable
。
import play.api.libs.json._
sealed trait SpeedUnit
case object Knots extends SpeedUnit
case object MetersPerSecond extends SpeedUnit
case object MilesPerHour extends SpeedUnit
object SpeedUnit {
implicit val speedUnitWrites: Writes[SpeedUnit] = new Writes[SpeedUnit] {
def writes(x: SpeedUnit) = Json.toJson(
x match {
case Knots => "KTS"
case MetersPerSecond => "MPS"
case MilesPerHour => "MPH"
}
)
}
}
case class Speed(value: Int, unit: SpeedUnit)
object Speed {
implicit val speedWrites: Writes[Speed] = new Writes[Speed] {
def writes(x: Speed) = Json.obj(
"value" -> x.value,
"speedUnit" -> x.unit
)
}
}
然后:
scala> Json.toJson(Speed(10, MilesPerHour))
res0: play.api.libs.json.JsValue = {"value":10,"speedUnit":"MPH"}
我已将 Writes
实例放在这两种类型的伴随对象中,但它们可以放在其他地方(例如,如果您不想在模型中混淆序列化问题)。
您还可以使用 Play JSON 的函数 API:
来简化(或至少简洁化)这一点sealed trait SpeedUnit
case object Knots extends SpeedUnit
case object MetersPerSecond extends SpeedUnit
case object MilesPerHour extends SpeedUnit
case class Speed(value: Int, unit: SpeedUnit)
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val speedWrites: Writes[Speed] = (
(__ \ 'value).write[Int] and
(__ \ 'speedUnit).write[String].contramap[SpeedUnit] {
case Knots => "KTS"
case MetersPerSecond => "MPS"
case MilesPerHour => "MPH"
}
)(unlift(Speed.unapply))
您采用哪种方法(功能性或显性)很大程度上取决于品味。