我有一个函数,它接受 json 字符串,解析它,根据条件获取一些值并转换不同类型的 JsValue:
trait Module
case class Module1(dp: String, qu: String, us: String) extends Module
case class Module2(dp: String, qu: String) extends Module
case class Module3(dp: String) extends Module
case class EmptyModule() extends Module
def getModule: (String, String) => String = (str1: String, str2: String) => {
modJsonString = if(str1.nonEmpty && str2.nonEmpty){
val modObject = Json.parse(str).as[JsObject]
val dp = (modObject \ "dp").as[String]
val qu = (modObject \ "qu").as[String]
val us = (modObject \ "us").as[String]
Module1(dp,qu,us)
} else if(str1.nonEmpty){
val dp = (modObject \ "dp").as[String]
val qu = (modObject \ "qu").as[String]
Module2(dp,qu)
} else if(str2.nonEmpty){
val dp = (modObject \ "dp").as[String]
Module2(dp)
}else EmptyModule()
implicit val mod = Json.format[Module]
val jsonValue = Json.toJson(modJsonString)
jsonValue.toString()
}
执行此函数时,出现错误:
No apply function found for Module
implicit val mod = Json.format[Module]
任何人都可以帮助格式化通用类或特征。
有多种方法可以为
Writes
实现 trait
(我不是在谈论 Reads
,因为你的示例只需要一个序列化器)。
“最常见”的是为每种可能的子类型实现一个,并为重用每个子类型的特征实现一个。
类似:
implicit val writes1: Writes[Module1] = ...
implicit val writes2: Writes[Module2] = ...
...
implicit val writes: Writes[Module] = Writes {
case m: Module1 => writes1.apply(m)
case m: Module2 => writes2.apply(m)
...
}
根据您是否需要子类型自己的序列化器,它们可以保留原样或设为私有,甚至内联到特征的序列化器中。
我相信还有其他方法,包括一些可以为您生成所有这些的库,但我不会在这里介绍这一点,因为我对它们不太了解。
话虽这么说,你的整个代码可能会被简化很多。
您的原始代码无法编译,并且存在一些未知数(我离开的地方
???
),但它可能如下所示:
implicit val format1: OFormat[Module1] = Json.format[Module1]
implicit val format2: OFormat[Module2] = Json.format[Module2]
...
def getModule: (String, String) => String = (str1: String, str2: String) => {
val modJsonString = if(str1.nonEmpty && str2.nonEmpty){
Json.parse(???).as[Module1]
} else if(str1.nonEmpty){
Json.parse(str1).as[Module2]
} else if(str2.nonEmpty){
Json.parse(str2).as[Module3]
}else EmptyModule()
Json.toJson(modJsonString).toString()
}
一般来说,
Json.format[???]
下面是一个宏,要求???
在伴生对象中定义有apply
和unapply
方法。案例类会自动满足此要求,但对于普通类和特征,您必须自己提供
所以你可以用这样的东西来实现它
import play.api.libs.json._
trait Module
object Module {
def apply(dp: String, qu: String, us: Option[String]): Module = {
us match {
case Some(us) => Module1(dp, qu, us)
case None => Module2(dp, qu)
}
}
def unapply(module: Module): Option[(String, String, Option[String])] = {
module match {
case Module1(dp, qu, us) => Some(dp, qu, Some(us))
case Module2(dp, qu) => Some(dp, qu, None)
}
}
implicit val format = Json.format[Module]
}
case class Module1(dp: String, qu: String, us: String) extends Module
case class Module2(dp: String, qu: String) extends Module
object Main extends App {
val module: Module = Module1("qwe", "qwe", "qwe")
val out = Module.format.writes(module)
println(out)
}
或者你甚至可以编写自己的
OFormat[Module]
,但这里还有更多的样板
trait Module
object Module {
implicit val format = OFormat[Module](
(jsV: JsValue) => {
// your custom read function
JsSuccess(Module1("qwe", "qwe", "qwe"))
},
(m: Module) => {
// your custom write function
JsObject(Seq("qwe" -> JsString(m.toString)))
}
)
}
Json.format 在 Play 文档中有更详细的描述:https://www.playframework.com/documentation/2.8.x/ScalaJsonAutomated#Requirements
另请注意,即使您应用此类修复,这也不会进一步推进您的斗争,因为您的代码还存在一些问题,例如:
val modObject
位于 if
范围内,因此不能在其之外使用val
getModule
目前是一个返回函数的函数,但您始终可以拥有一个接受两个参数并返回一个参数的函数(即 def getModule(str1: String, str2: String): String
)我不知道你的目标和你提供的代码的逻辑是什么,但无论它们是什么,我希望你会玩得开心,因为我相信这是编程中最重要的部分