播放JSON序列化/反序列化

时间:2017-06-07 12:58:21

标签: json scala playframework

我试图为此创建一些格式:

  case class Work[T](todo: Seq[T], failed: Seq[T], success: Seq[T])

  object Work {
    implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = Json.format[Work[T]]
  }

  object InternalMessage {
    implicit def format[D, R](implicit
                              rD: Reads[D],
                              wD: Writes[D],
                              rR: Reads[R],
                              wR: Writes[R]
                             ): Format[InternalMessage[D, R]] = Json.format[InternalMessage[D, R]]

  }

  case class InternalMessage[D, R](
                                    download: List[Work[D]],
                                    refine: List[Work[R]],
                                    numberOfTries: Int
                                  )

这不起作用,我不明白为什么。错误是

[error] /home/oleber/develop/data-platform/modules/importerTemplate/src/main/scala/template/TemplateModel.scala:46: No apply function found matching unapply parameters
[error]     implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = Json.format[Work[T]]
[error]                                                                                              ^
[error] /home/oleber/develop/data-platform/modules/importerTemplate/src/main/scala/template/TemplateModel.scala:55: No apply function found matching unapply parameters
[error]                              ): Format[InternalMessage[D, R]] = Json.format[InternalMessage[D, R]]

感谢您的帮助

1 个答案:

答案 0 :(得分:0)

AFAIU您无法使用Play 2.5宏为此类数据类型生成JSON Format。限制你的是你使用Seq[T],即你想要一个泛型类的JSON序列化,该泛型类的字段使用从该泛型类型构建的复杂类型,而不仅仅是原始的通用T。相关代码似乎在JsMacroImpl lines 135-141中。在paramsMatch内部maybeApply计算方法内的那些行中,宏检查是否存在具有匹配(补充)签名的applyunapply方法:

val maybeApply = applies.collectFirst {
  case (apply: MethodSymbol) if hasVarArgs && {
    // Option[List[c.universe.Type]]
    val someApplyTypes = apply.paramLists.headOption.map(_.map(_.asTerm.typeSignature))
    val someInitApply = someApplyTypes.map(_.init)
    val someApplyLast = someApplyTypes.map(_.last)
    val someInitUnapply = unapplyReturnTypes.map(_.init)
    val someUnapplyLast = unapplyReturnTypes.map(_.last)
    val initsMatch = someInitApply == someInitUnapply
    val lastMatch = (for {
      lastApply <- someApplyLast
      lastUnapply <- someUnapplyLast
    } yield lastApply <:< lastUnapply).getOrElse(false)
    initsMatch && lastMatch
  } => apply

  case (apply: MethodSymbol) if {
    val applyParams = apply.paramLists.headOption.
      toList.flatten.map(_.typeSignature)
    val unapplyParams = unapplyReturnTypes.toList.flatten

    def paramsMatch = (applyParams, unapplyParams).zipped.forall {
      case (TypeRef(NoPrefix, applyParam, _),
        TypeRef(NoPrefix, unapplyParam, _)) => // for generic parameter
        applyParam.fullName == unapplyParam.fullName

      case (applyParam, unapplyParam) => applyParam =:= unapplyParam
    }

    (applyParams.size == unapplyParams.size && paramsMatch)
  } => apply
}

正如您在相关(非hasVarArgs分支)中所看到的,此代码仅处理两种情况:applyunapply中的类型完全匹配或同一原始时使用泛型类型。这里没有处理Seq[T](以及其他复杂泛型类型)的情况,当maybeApply为空时,只会产生几行错误:

val (tparams, params) = maybeApply match {
  case Some(apply) => {
    apply.typeParams -> apply.paramLists.head
    // assume there is a single parameter group
  }

  case None => c.abort(c.enclosingPosition, "No apply function found matching unapply parameters")
}

在Play-JSON 2.6中,这段代码经过了大量的重复处理,现在似乎也支持你的案例(参见conforms inner method)。

如果升级到(尚未发布)Play-JSON 2.6是不可接受的,您可以使用JSON Reads/Writes/Format Combinators doc自己创建Formats个对象。这样的事情对你有用:

  implicit def format[T](implicit r: Reads[T], w: Writes[T]): Format[Work[T]] = {
    val workReads = (
      (JsPath \ "todo").read[Seq[T]] and
        (JsPath \ "failed").read[Seq[T]] and
        (JsPath \ "success").read[Seq[T]]
      ) (Work.apply[T] _)

    val workWrites = (
      (JsPath \ "todo").write[Seq[T]] and
        (JsPath \ "failed").write[Seq[T]] and
        (JsPath \ "success").write[Seq[T]]
      ) (unlift(Work.unapply[T]))

    new Format[Work[T]] {
      override def reads(json: JsValue): JsResult[Work[T]] = workReads.reads(json)

      override def writes(o: Work[T]): JsValue = workWrites.writes(o)
    }
  }