Scala 使用类型参数在AST上播放json
我正在尝试为AST创建play json读写,基本上如下所示Scala 使用类型参数在AST上播放json,scala,playframework,playframework-2.0,shapeless,play-json,Scala,Playframework,Playframework 2.0,Shapeless,Play Json,我正在尝试为AST创建play json读写,基本上如下所示 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] exten
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)
}
我的尝试看起来是这样的
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)
}
这给我留下了两个问题
第一个是,如果我在Rule.ruleReads中插入我认为正确的表达式,对于?
,Json.reads[NoValueRule[R]]
和Json.reads[ValueRule[R,V]]
的两个实例,我分别得到以下编译错误
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[\u]]
的规则和我定义的隐式读取之间的区别,隐式读取应该覆盖任何特定的规则,但不能是任何规则
你知道我该怎么做吗?我觉得这应该是可能的,但可能不是。尽管我认为您应该尝试一些基于宏的库,可以通过谷歌搜索“play json sealed trait”找到,例如,这里有一个手写的解决方案,可能适合您:
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")
}
}
然后,测试
变成:
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 sealed trait”找到,例如,这里有一个可能适合您的手写解决方案:
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")
}
}
然后,测试
变成:
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
您还可以描述一下您期望结果JSON的样子吗?你有已知的固定格式吗?或者任何可以用Scala代码编码和解码的东西都可以吗?问题在于,您的规则
子类型在形状上非常相似,因此显而易见的解决方案是在JSON中添加一些显式类型鉴别器。可以吗?或者你真的想通过JSON的细微差别来区分实际的类型吗?在JSON中添加一些没有显式出现在case类中的东西是完全好的,尽管我还没有尝试过,我想在这种情况下,我必须为每一个写定制的读写。你能描述一下你期望JSON的结果是什么样的吗?你有已知的固定格式吗?或者任何可以用Scala代码编码和解码的东西都可以吗?问题在于,您的规则
子类型在形状上非常相似,因此显而易见的解决方案是在JSON中添加一些显式类型鉴别器。可以吗?或者你真的想通过JSON中细微的差异来区分实际类型吗?在JSON中添加一些没有显式出现在case类中的内容是完全可以的,虽然我还没有尝试过,但我想在这种情况下,我必须为每种内容编写自定义的读写操作。这似乎非常有效,谢谢。希望它不是那么多代码,但它确实做了我希望它做的事情,谢谢。你认为你可以通过为每个规则和模式匹配定义枚举来绕过反射吗?@Ir1sh,我不确定你到底想避免什么,你称之为“反射”。如果您的意思是,除了Class.getSimpleName
/Class.getName
,您可以显式地提供一些其他名称-是的,您显然可以。如果这是其他内容,您应该更明确地说明。@Ir1sh,不,这是“不安全的”,因为格式的是读取的组合+OWrites
,是不变的,所以我必须使用显式强制转换格式。写入(o.asInstanceOf[R])
这取决于这样一个事实,即我放在那里的实际上是正确的子类型。如果将CompoundFormat
设置为非泛型,并使用显式模式匹配而不是innerFormatsByClass
,我认为可以避免这种转换。P.S.ClassTag
由编译器在编译时解析。我所需要的只是一个runtimeClass
,在Java中,我必须显式地传递给addSubType
。在Scala中,我可以使用这个技巧。@Ir1sh,为了澄清我的观点,我在我的答案中添加了非泛型explicictruleformat
code。我相信它实际上与我最初的实现相同,但不同之处在于它是专门针对规则[\u]
特性定制的。这似乎很有效,谢谢。希望它不是那么多代码,但它确实做了我希望它做的事情,谢谢。你认为你可以通过为每个规则和模式匹配定义枚举来绕过反射吗?@Ir1sh,我不确定你到底想避免什么,你称之为“反射”。如果您的意思是,除了Class.getSimpleName
/Class.getName
,您可以显式地提供一些其他名称-是的,您显然可以。如果这是其他内容,您应该更明确地说明。@Ir1sh,不,这是“不安全的”,因为格式的是读取的组合+OWrites
,是不变的,所以我必须使用显式强制转换格式。写入(o.asInstanceOf[R])
这取决于这样一个事实,即我放在那里的实际上是正确的子类型。如果将CompoundFormat
设置为非泛型,并使用显式模式匹配而不是innerFormatsByClass
,我认为可以避免这种转换。P.S.ClassTag
由编译器在编译时解析。我所需要的只是一个runtimeClass
,在Java中,我必须显式地传递给addSubType
。在Scala中,我可以使用这个技巧。@Ir1sh,为了澄清我的观点,我在我的答案中添加了非泛型explicictruleformat
code。我相信它实际上与我最初的实现相同,但不同之处在于它是ta