在带有类型参数的 AST 上播放-json
play-json on AST with type parameters
我正在尝试创建 play-json 读取和写入一个基本上看起来像这样的 AST
abstract sealed trait Rule[A] {
def roomId: Option[Long] = None
def valid(in: A): Boolean
}
abstract sealed trait ValueRule[A, B] extends Rule[A] {
def value: B
}
abstract sealed trait NoValueRule[A] extends Rule[A]
case class OnlyDuringWorkHours(override val roomId: Option[Long] = None) extends NoValueRule[((ResStart, ResEnd), Center)] {
override def valid(in: ((ResStart, ResEnd), Center)): Boolean = true
}
case class MaxLeadTime(override val roomId: Option[Long] = None, override val value: Int) extends ValueRule[ResStart, Int] {
override def valid(in: ResStart): Boolean = true
}
case class MaxDuration(override val roomId: Option[Long] = None, override val value: String) extends ValueRule[(ResStart, ResEnd), String] {
override def valid(in: (ResStart, ResEnd)): Boolean = true
}
case class Rules(centerId: Long, ruleList: Seq[Rule[_]])
我的尝试看起来像这样
object Rule {
implicit def ruleReads[R, V](implicit rReads: Reads[R], vReads: Reads[V] = null): Reads[Rule[R]] = {
val theVRead = Option(vReads)
val nvr = ???
if (Option(theVRead).isDefined) {
val vr = ???
__.read[ValueRule[R, V]](vr).map(x => x.asInstanceOf[Rule[R]]).orElse(__.read[NoValueRule[R]](nvr).map(x => x.asInstanceOf[Rule[R]]))
} else {
__.read[NoValueRule[R]](nvr).map(x => x.asInstanceOf[Rule[R]])
}
}
implicit def ruleWrites[R, V](implicit rWrites: Writes[R], vWrites: Writes[V] = null): Writes[Rule[R]] = Writes[Rule[R]]{
case nv: NoValueRule[R] => Json.writes[NoValueRule[R]].writes(nv)
case v: ValueRule[R, V] => Json.writes[ValueRule[R, V]].writes(v)
}
}
object ValueRule {
implicit def valueRuleReads[R, V](implicit rReads: Reads[R], vReads: Reads[V]): Reads[ValueRule[R, V]] = {
val mlt = Json.reads[MaxLeadTime]
val md = Json.reads[MaxDuration]
__.read[MaxDuration](md).map(x => x.asInstanceOf[ValueRule[R, V]])
.orElse(
__.read[MaxLeadTime](mlt).map(x => x.asInstanceOf[ValueRule[R, V]])
)
}
implicit def valueRuleWrites[R, V](implicit rWrites: Writes[R], vWrites: Writes[V]): Writes[ValueRule[R, V]] = Writes[ValueRule[R, V]]{
case mlt: MaxLeadTime => Json.writes[MaxLeadTime].writes(mlt)
case md: MaxDuration => Json.writes[MaxDuration].writes(md)
}
}
object NoValueRule {
implicit def noValueRuleReads[R](implicit rReads: Reads[R]): Reads[NoValueRule[R]] = {
val odwh = Json.reads[OnlyDuringWorkHours]
__.read[OnlyDuringWorkHours](odwh).map(x => x.asInstanceOf[NoValueRule[R]])
}
implicit def noValueRuleWrites[R](implicit rWrites: Writes[R]): Writes[NoValueRule[R]] = Writes[NoValueRule[R]]{
case odwh: OnlyDuringWorkHours => Json.writes[OnlyDuringWorkHours].writes(odwh)
}
}
object OnlyDuringWorkHours {
implicit val format: Format[OnlyDuringWorkHours] = Json.format[OnlyDuringWorkHours]
}
object MaxLeadTime {
implicit val format: Format[MaxLeadTime] = Json.format[MaxLeadTime]
}
object MaxDuration {
implicit val format: Format[MaxDuration] = Json.format[MaxDuration]
}
object Rules {
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
implicit val rulesReads: Reads[Rules] = (
(JsPath \ "centerId").read[Long] and
(JsPath \ "ruleList").read[Seq[Rule]]
)(Rules.apply _)
implicit val rulesWrites: Writes[Rules] = (
(JsPath \ "centerId").write[Long] and
???
)(unlift(Rules.unapply))
implicit val format: Format[Rules] = Format(rulesReads, rulesWrites)
}
这给我留下了两个问题。
首先,如果我分别为 ???
、Json.reads[NoValueRule[R]]
和 Json.reads[ValueRule[R, V]]
的两个实例插入我认为在 Rule.ruleReads 中正确的表达式,我会得到以下编译错误
cmd16.sc:8: type mismatch;
found : play.api.libs.json.JsResult[Helper.this.OnlyDuringWorkHours]
required: play.api.libs.json.JsResult[Helper.this.NoValueRule[R]]
val nvr = Json.reads[NoValueRule[R]]
^cmd16.sc:11: type mismatch;
found : play.api.libs.json.JsResult[Helper.this.MaxLeadTime]
required: play.api.libs.json.JsResult[Helper.this.ValueRule[R,V]]
val vr = Json.reads[ValueRule[R, V]]
^
第二个是如果我离开 ???
以便该部分编译它然后无法使用
编译规则对象
cmd17.sc:71: No Json deserializer found for type Seq[cmd17Wrapper.this.cmd16.wrapper.Rule]. Try to implement an implicit Reads or Format for this type.
(JsPath \ "ruleList").read[Seq[Rule]]
^
我可以改为让规则读取/写入格式并得到非常相似的错误
我认为 2 的问题在于包含 Seq[Rule[_]]
的规则与我定义的隐式读取之间的区别,该读取应涵盖任何 特定的 规则,但不包括可以是任何东西
我有什么想法可以让它工作吗?我觉得这应该是可能的,但也许不是。
虽然我认为您应该尝试一些 macro-based 库,可以通过谷歌搜索 "play json sealed trait" 找到 Play JSON Derived Codecs ,这里有一个 hand-written 可能有效的解决方案给你:
object PlayJson {
import play.api.libs.json._
// fake types instead of your real ones
type ResStart = Int
type ResEnd = Int
type Center = Int
sealed trait Rule[A] {
def roomId: Option[Long] = None
def valid(in: A): Boolean
}
sealed trait ValueRule[A, B] extends Rule[A] {
def value: B
}
sealed trait NoValueRule[A] extends Rule[A]
case class OnlyDuringWorkHours(override val roomId: Option[Long] = None) extends NoValueRule[((ResStart, ResEnd), Center)] {
override def valid(in: ((ResStart, ResEnd), Center)): Boolean = true
}
case class MaxLeadTime(override val roomId: Option[Long] = None, override val value: Int) extends ValueRule[ResStart, Int] {
override def valid(in: ResStart): Boolean = true
}
case class MaxDuration(override val roomId: Option[Long] = None, override val value: String) extends ValueRule[(ResStart, ResEnd), String] {
override def valid(in: (ResStart, ResEnd)): Boolean = true
}
case class Rules(centerId: Long, ruleList: Seq[Rule[_]])
object CompoundFormat {
final val discriminatorKey = "$type$"
private case class UnsafeFormatWrapper[U, R <: U : ClassTag](format: OFormat[R]) extends OFormat[U] {
def typeName: String = {
val clazz = implicitly[ClassTag[R]].runtimeClass
try {
clazz.getSimpleName
}
catch {
// getSimpleName might fail for inner classes because of the name mangling
case _: InternalError => clazz.getName
}
}
override def reads(json: JsValue): JsResult[U] = format.reads(json)
override def writes(o: U): JsObject = {
val base = format.writes(o.asInstanceOf[R])
base + (discriminatorKey, JsString(typeName))
}
}
}
class CompoundFormat[A]() extends OFormat[A] {
import CompoundFormat._
private val innerFormatsByName = mutable.Map.empty[String, UnsafeFormatWrapper[A, _]]
private val innerFormatsByClass = mutable.Map.empty[Class[_], UnsafeFormatWrapper[A, _]]
override def reads(json: JsValue): JsResult[A] = {
val jsObject = json.asInstanceOf[JsObject]
val name = jsObject(discriminatorKey).asInstanceOf[JsString].value
val innerFormat = innerFormatsByName.getOrElse(name, throw new RuntimeException(s"Unknown child type $name"))
innerFormat.reads(jsObject)
}
override def writes(o: A): JsObject = {
val innerFormat = innerFormatsByClass.getOrElse(o.getClass, throw new RuntimeException(s"Unknown child type ${o.getClass}"))
innerFormat.writes(o)
}
def addSubType[R <: A : ClassTag](format: OFormat[R]): Unit = {
val wrapper = new UnsafeFormatWrapper[A, R](format)
innerFormatsByName.put(wrapper.typeName, wrapper)
innerFormatsByClass.put(implicitly[ClassTag[R]].runtimeClass, wrapper)
}
}
def buildRuleFormat: OFormat[Rule[_]] = {
val compoundFormat = new CompoundFormat[Rule[_]]
compoundFormat.addSubType(Json.format[OnlyDuringWorkHours])
compoundFormat.addSubType(Json.format[MaxLeadTime])
compoundFormat.addSubType(Json.format[MaxDuration])
compoundFormat
}
def test(): Unit = {
implicit val ruleFormat = buildRuleFormat
implicit val rulesFormat = Json.format[Rules]
val rules0 = Rules(1, List(
OnlyDuringWorkHours(Some(1)),
MaxLeadTime(Some(2), 2),
MaxDuration(Some(3), "Abc")
))
val json = Json.toJsObject(rules0)
println(s"encoded: '$json'")
val rulesDecoded = Json.fromJson[Rules](json)
println(s"decoded: $rulesDecoded")
}
}
调用PlayJson.test
打印
encoded: '{"centerId":1,"ruleList":[{"roomId":1,"$type$":"OnlyDuringWorkHours"},{"roomId":2,"value":2,"$type$":"MaxLeadTime"},{"roomId":3,"value":"Abc","$type$":"MaxDuration"}]}'
decoded: JsSuccess(Rules(1,List(OnlyDuringWorkHours(Some(1)), MaxLeadTime(Some(2),2), MaxDuration(Some(3),Abc))),)
主要思想是CompoundFormat
用于存储class名称和每个child对应的OFormat
之间映射的密封特性。
更新(关于反射问题)
这是 CompoundFormat
的 non-generic 版本,我希望它类似于 macro-based 库可以生成的内容(实际上我希望好的 macro-based 库也可以处理密封特征的某些 children 是单例 object
而不是此代码不处理的 class
的情况):
object ExplicitRuleFormat {
implicit val format: OFormat[Rule[_]] = new ExplicitRuleFormat()
private object InnerFormats {
final val discriminatorKey = "$type$"
implicit val onlyDuringWorkHoursFormat = Json.format[OnlyDuringWorkHours]
final val onlyDuringWorkHoursTypeName = "OnlyDuringWorkHours"
implicit val maxLeadTimeFormat = Json.format[MaxLeadTime]
final val maxLeadTimeTypeName = "MaxLeadTime"
implicit val maxDurationFormat = Json.format[MaxDuration]
final val maxDurationTypeName = "MaxDuration"
}
}
class ExplicitRuleFormat extends OFormat[Rule[_]] {
import ExplicitRuleFormat.InnerFormats._
override def reads(json: JsValue): JsResult[Rule[_]] = {
val jsObject = json.asInstanceOf[JsObject]
val name = jsObject(discriminatorKey).asInstanceOf[JsString].value
name match {
case s if onlyDuringWorkHoursTypeName.equals(s) => Json.fromJson[OnlyDuringWorkHours](jsObject)
case s if maxLeadTimeTypeName.equals(s) => Json.fromJson[MaxLeadTime](jsObject)
case s if maxDurationTypeName.equals(s) => Json.fromJson[MaxDuration](jsObject)
}
}
override def writes(r: Rule[_]): JsObject = r match {
case rr: OnlyDuringWorkHours => writeImpl(rr, onlyDuringWorkHoursTypeName)
case rr: MaxLeadTime => writeImpl(rr, maxLeadTimeTypeName)
case rr: MaxDuration => writeImpl(rr, maxDurationTypeName)
}
def writeImpl[R <: Rule[_]](r: R, typeName: String)(implicit w: OWrites[R]): JsObject = {
Json.toJsObject(r) + (discriminatorKey, JsString(typeName))
}
}
然后 test
变成:
def test(): Unit = {
import ExplicitRuleFormat.format
implicit val rulesFormat = Json.format[Rules]
val rules0 = Rules(1, List(
OnlyDuringWorkHours(Some(1)),
MaxLeadTime(Some(2), 2),
MaxDuration(Some(3), "Abc")
))
val json = Json.toJsObject(rules0)
println(s"encoded: '$json'")
val rulesDecoded = Json.fromJson[Rules](json)
println(s"decoded: $rulesDecoded")
}
实际上,您只需将 implicit val ruleFormat = buildRuleFormat
替换为 import ExplicitRuleFormat.format
。
我正在尝试创建 play-json 读取和写入一个基本上看起来像这样的 AST
abstract sealed trait Rule[A] {
def roomId: Option[Long] = None
def valid(in: A): Boolean
}
abstract sealed trait ValueRule[A, B] extends Rule[A] {
def value: B
}
abstract sealed trait NoValueRule[A] extends Rule[A]
case class OnlyDuringWorkHours(override val roomId: Option[Long] = None) extends NoValueRule[((ResStart, ResEnd), Center)] {
override def valid(in: ((ResStart, ResEnd), Center)): Boolean = true
}
case class MaxLeadTime(override val roomId: Option[Long] = None, override val value: Int) extends ValueRule[ResStart, Int] {
override def valid(in: ResStart): Boolean = true
}
case class MaxDuration(override val roomId: Option[Long] = None, override val value: String) extends ValueRule[(ResStart, ResEnd), String] {
override def valid(in: (ResStart, ResEnd)): Boolean = true
}
case class Rules(centerId: Long, ruleList: Seq[Rule[_]])
我的尝试看起来像这样
object Rule {
implicit def ruleReads[R, V](implicit rReads: Reads[R], vReads: Reads[V] = null): Reads[Rule[R]] = {
val theVRead = Option(vReads)
val nvr = ???
if (Option(theVRead).isDefined) {
val vr = ???
__.read[ValueRule[R, V]](vr).map(x => x.asInstanceOf[Rule[R]]).orElse(__.read[NoValueRule[R]](nvr).map(x => x.asInstanceOf[Rule[R]]))
} else {
__.read[NoValueRule[R]](nvr).map(x => x.asInstanceOf[Rule[R]])
}
}
implicit def ruleWrites[R, V](implicit rWrites: Writes[R], vWrites: Writes[V] = null): Writes[Rule[R]] = Writes[Rule[R]]{
case nv: NoValueRule[R] => Json.writes[NoValueRule[R]].writes(nv)
case v: ValueRule[R, V] => Json.writes[ValueRule[R, V]].writes(v)
}
}
object ValueRule {
implicit def valueRuleReads[R, V](implicit rReads: Reads[R], vReads: Reads[V]): Reads[ValueRule[R, V]] = {
val mlt = Json.reads[MaxLeadTime]
val md = Json.reads[MaxDuration]
__.read[MaxDuration](md).map(x => x.asInstanceOf[ValueRule[R, V]])
.orElse(
__.read[MaxLeadTime](mlt).map(x => x.asInstanceOf[ValueRule[R, V]])
)
}
implicit def valueRuleWrites[R, V](implicit rWrites: Writes[R], vWrites: Writes[V]): Writes[ValueRule[R, V]] = Writes[ValueRule[R, V]]{
case mlt: MaxLeadTime => Json.writes[MaxLeadTime].writes(mlt)
case md: MaxDuration => Json.writes[MaxDuration].writes(md)
}
}
object NoValueRule {
implicit def noValueRuleReads[R](implicit rReads: Reads[R]): Reads[NoValueRule[R]] = {
val odwh = Json.reads[OnlyDuringWorkHours]
__.read[OnlyDuringWorkHours](odwh).map(x => x.asInstanceOf[NoValueRule[R]])
}
implicit def noValueRuleWrites[R](implicit rWrites: Writes[R]): Writes[NoValueRule[R]] = Writes[NoValueRule[R]]{
case odwh: OnlyDuringWorkHours => Json.writes[OnlyDuringWorkHours].writes(odwh)
}
}
object OnlyDuringWorkHours {
implicit val format: Format[OnlyDuringWorkHours] = Json.format[OnlyDuringWorkHours]
}
object MaxLeadTime {
implicit val format: Format[MaxLeadTime] = Json.format[MaxLeadTime]
}
object MaxDuration {
implicit val format: Format[MaxDuration] = Json.format[MaxDuration]
}
object Rules {
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
implicit val rulesReads: Reads[Rules] = (
(JsPath \ "centerId").read[Long] and
(JsPath \ "ruleList").read[Seq[Rule]]
)(Rules.apply _)
implicit val rulesWrites: Writes[Rules] = (
(JsPath \ "centerId").write[Long] and
???
)(unlift(Rules.unapply))
implicit val format: Format[Rules] = Format(rulesReads, rulesWrites)
}
这给我留下了两个问题。
首先,如果我分别为 ???
、Json.reads[NoValueRule[R]]
和 Json.reads[ValueRule[R, V]]
的两个实例插入我认为在 Rule.ruleReads 中正确的表达式,我会得到以下编译错误
cmd16.sc:8: type mismatch;
found : play.api.libs.json.JsResult[Helper.this.OnlyDuringWorkHours]
required: play.api.libs.json.JsResult[Helper.this.NoValueRule[R]]
val nvr = Json.reads[NoValueRule[R]]
^cmd16.sc:11: type mismatch;
found : play.api.libs.json.JsResult[Helper.this.MaxLeadTime]
required: play.api.libs.json.JsResult[Helper.this.ValueRule[R,V]]
val vr = Json.reads[ValueRule[R, V]]
^
第二个是如果我离开 ???
以便该部分编译它然后无法使用
cmd17.sc:71: No Json deserializer found for type Seq[cmd17Wrapper.this.cmd16.wrapper.Rule]. Try to implement an implicit Reads or Format for this type.
(JsPath \ "ruleList").read[Seq[Rule]]
^
我可以改为让规则读取/写入格式并得到非常相似的错误
我认为 2 的问题在于包含 Seq[Rule[_]]
的规则与我定义的隐式读取之间的区别,该读取应涵盖任何 特定的 规则,但不包括可以是任何东西
我有什么想法可以让它工作吗?我觉得这应该是可能的,但也许不是。
虽然我认为您应该尝试一些 macro-based 库,可以通过谷歌搜索 "play json sealed trait" 找到 Play JSON Derived Codecs ,这里有一个 hand-written 可能有效的解决方案给你:
object PlayJson {
import play.api.libs.json._
// fake types instead of your real ones
type ResStart = Int
type ResEnd = Int
type Center = Int
sealed trait Rule[A] {
def roomId: Option[Long] = None
def valid(in: A): Boolean
}
sealed trait ValueRule[A, B] extends Rule[A] {
def value: B
}
sealed trait NoValueRule[A] extends Rule[A]
case class OnlyDuringWorkHours(override val roomId: Option[Long] = None) extends NoValueRule[((ResStart, ResEnd), Center)] {
override def valid(in: ((ResStart, ResEnd), Center)): Boolean = true
}
case class MaxLeadTime(override val roomId: Option[Long] = None, override val value: Int) extends ValueRule[ResStart, Int] {
override def valid(in: ResStart): Boolean = true
}
case class MaxDuration(override val roomId: Option[Long] = None, override val value: String) extends ValueRule[(ResStart, ResEnd), String] {
override def valid(in: (ResStart, ResEnd)): Boolean = true
}
case class Rules(centerId: Long, ruleList: Seq[Rule[_]])
object CompoundFormat {
final val discriminatorKey = "$type$"
private case class UnsafeFormatWrapper[U, R <: U : ClassTag](format: OFormat[R]) extends OFormat[U] {
def typeName: String = {
val clazz = implicitly[ClassTag[R]].runtimeClass
try {
clazz.getSimpleName
}
catch {
// getSimpleName might fail for inner classes because of the name mangling
case _: InternalError => clazz.getName
}
}
override def reads(json: JsValue): JsResult[U] = format.reads(json)
override def writes(o: U): JsObject = {
val base = format.writes(o.asInstanceOf[R])
base + (discriminatorKey, JsString(typeName))
}
}
}
class CompoundFormat[A]() extends OFormat[A] {
import CompoundFormat._
private val innerFormatsByName = mutable.Map.empty[String, UnsafeFormatWrapper[A, _]]
private val innerFormatsByClass = mutable.Map.empty[Class[_], UnsafeFormatWrapper[A, _]]
override def reads(json: JsValue): JsResult[A] = {
val jsObject = json.asInstanceOf[JsObject]
val name = jsObject(discriminatorKey).asInstanceOf[JsString].value
val innerFormat = innerFormatsByName.getOrElse(name, throw new RuntimeException(s"Unknown child type $name"))
innerFormat.reads(jsObject)
}
override def writes(o: A): JsObject = {
val innerFormat = innerFormatsByClass.getOrElse(o.getClass, throw new RuntimeException(s"Unknown child type ${o.getClass}"))
innerFormat.writes(o)
}
def addSubType[R <: A : ClassTag](format: OFormat[R]): Unit = {
val wrapper = new UnsafeFormatWrapper[A, R](format)
innerFormatsByName.put(wrapper.typeName, wrapper)
innerFormatsByClass.put(implicitly[ClassTag[R]].runtimeClass, wrapper)
}
}
def buildRuleFormat: OFormat[Rule[_]] = {
val compoundFormat = new CompoundFormat[Rule[_]]
compoundFormat.addSubType(Json.format[OnlyDuringWorkHours])
compoundFormat.addSubType(Json.format[MaxLeadTime])
compoundFormat.addSubType(Json.format[MaxDuration])
compoundFormat
}
def test(): Unit = {
implicit val ruleFormat = buildRuleFormat
implicit val rulesFormat = Json.format[Rules]
val rules0 = Rules(1, List(
OnlyDuringWorkHours(Some(1)),
MaxLeadTime(Some(2), 2),
MaxDuration(Some(3), "Abc")
))
val json = Json.toJsObject(rules0)
println(s"encoded: '$json'")
val rulesDecoded = Json.fromJson[Rules](json)
println(s"decoded: $rulesDecoded")
}
}
调用PlayJson.test
打印
encoded: '{"centerId":1,"ruleList":[{"roomId":1,"$type$":"OnlyDuringWorkHours"},{"roomId":2,"value":2,"$type$":"MaxLeadTime"},{"roomId":3,"value":"Abc","$type$":"MaxDuration"}]}'
decoded: JsSuccess(Rules(1,List(OnlyDuringWorkHours(Some(1)), MaxLeadTime(Some(2),2), MaxDuration(Some(3),Abc))),)
主要思想是CompoundFormat
用于存储class名称和每个child对应的OFormat
之间映射的密封特性。
更新(关于反射问题)
这是 CompoundFormat
的 non-generic 版本,我希望它类似于 macro-based 库可以生成的内容(实际上我希望好的 macro-based 库也可以处理密封特征的某些 children 是单例 object
而不是此代码不处理的 class
的情况):
object ExplicitRuleFormat {
implicit val format: OFormat[Rule[_]] = new ExplicitRuleFormat()
private object InnerFormats {
final val discriminatorKey = "$type$"
implicit val onlyDuringWorkHoursFormat = Json.format[OnlyDuringWorkHours]
final val onlyDuringWorkHoursTypeName = "OnlyDuringWorkHours"
implicit val maxLeadTimeFormat = Json.format[MaxLeadTime]
final val maxLeadTimeTypeName = "MaxLeadTime"
implicit val maxDurationFormat = Json.format[MaxDuration]
final val maxDurationTypeName = "MaxDuration"
}
}
class ExplicitRuleFormat extends OFormat[Rule[_]] {
import ExplicitRuleFormat.InnerFormats._
override def reads(json: JsValue): JsResult[Rule[_]] = {
val jsObject = json.asInstanceOf[JsObject]
val name = jsObject(discriminatorKey).asInstanceOf[JsString].value
name match {
case s if onlyDuringWorkHoursTypeName.equals(s) => Json.fromJson[OnlyDuringWorkHours](jsObject)
case s if maxLeadTimeTypeName.equals(s) => Json.fromJson[MaxLeadTime](jsObject)
case s if maxDurationTypeName.equals(s) => Json.fromJson[MaxDuration](jsObject)
}
}
override def writes(r: Rule[_]): JsObject = r match {
case rr: OnlyDuringWorkHours => writeImpl(rr, onlyDuringWorkHoursTypeName)
case rr: MaxLeadTime => writeImpl(rr, maxLeadTimeTypeName)
case rr: MaxDuration => writeImpl(rr, maxDurationTypeName)
}
def writeImpl[R <: Rule[_]](r: R, typeName: String)(implicit w: OWrites[R]): JsObject = {
Json.toJsObject(r) + (discriminatorKey, JsString(typeName))
}
}
然后 test
变成:
def test(): Unit = {
import ExplicitRuleFormat.format
implicit val rulesFormat = Json.format[Rules]
val rules0 = Rules(1, List(
OnlyDuringWorkHours(Some(1)),
MaxLeadTime(Some(2), 2),
MaxDuration(Some(3), "Abc")
))
val json = Json.toJsObject(rules0)
println(s"encoded: '$json'")
val rulesDecoded = Json.fromJson[Rules](json)
println(s"decoded: $rulesDecoded")
}
实际上,您只需将 implicit val ruleFormat = buildRuleFormat
替换为 import ExplicitRuleFormat.format
。