如何使用 play json api 将泛型类型格式化为 json?

问题描述 投票:0回答:2

我有一个函数,它接受 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]

任何人都可以帮助格式化通用类或特征。

scala playframework play-json
2个回答
2
投票

有多种方法可以为

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()
}

0
投票

一般来说,

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
    范围内,因此不能在其之外使用
  • 您在第 7 行错过了
    val
  • getModule
    目前是一个返回函数的函数,但您始终可以拥有一个接受两个参数并返回一个参数的函数(即
    def getModule(str1: String, str2: String): String

我不知道你的目标和你提供的代码的逻辑是什么,但无论它们是什么,我希望你会玩得开心,因为我相信这是编程中最重要的部分

© www.soinside.com 2019 - 2024. All rights reserved.