Scala Play:列表到 Json-Array

Scala Play: List to Json-Array

我有一个包含一些个性化对象的列表。后者是这样定义的:

  sealed case class Personalization(firstname: String, lastname: String, keycardId: String)

我需要将此列表映射到 Json-数组结构,它必须如下所示:

"personalization": [
    {
      "id": "firstname",
      "value": "John"
    },
    {
      "id": "lastname",
      "value": "Doe"
    }...

我正在努力将字段信息映射到 id/value 对。通常,我会从个性化 class 中创建一个 play.api.libs.json.Format 并让它自动映射 -> Json.format[Personalization] - 但这次,我需要创建一个数组,其中一个条目可以包含 n 个属性.

所以求教,是否有可能使用Scala Play-Framework? 非常感谢任何输入,谢谢!

也许可以用我做的更优雅的方式来做,但你可以使用以下代码片段:

case class Field(id: String, value: String)

object Field {
  implicit val fieldFormatter: Format[Field] = Json.format[Field]
}

sealed case class Personalization(firstname: String, lastname: String, keycardId: String)

object Personalization {

  implicit val personalizationFormatter: Format[Personalization] = new Format[Personalization] {
    override def reads(json:  JsValue): JsResult[Personalization] =
      Try {
        val data = (json \ "personalization").as[JsValue]
        data match {
          case JsArray(value) =>
            val fields = value.map(_.as[Field]).map(f => f.id -> f.value).toMap
            val firstname = fields.getOrElse("firstname", throw new IllegalArgumentException("Mandatory field firstname is absent."))
            val lastname = fields.getOrElse("lastname", throw new IllegalArgumentException("Mandatory field lastname is absent."))
            val keycardId = fields.getOrElse("keycardId", throw new IllegalArgumentException("Mandatory field keycardId is absent."))
            Personalization(firstname, lastname, keycardId)
          case _ => throw new IllegalArgumentException("Incorrect json format for Personalization.")
        }
      }.toEither.fold(e => JsError(e.getMessage), JsSuccess(_))

    override def writes(o:  Personalization): JsValue = {
      val fields = List(Field("firstname", o.firstname), Field("lastname", o.lastname), Field("keycardId", o.keycardId))
      JsObject(List("personalization" -> Json.toJson(fields)))
    }
  }
}

它将{"personalization":[{"id":"firstname","value":"John"},{"id":"lastname","value":"Doe"},{"id":"keycardId","value":"1234"}]}转换为Personalization(John,Doe,1234),反之亦然

这样写 JSON 表示并不复杂,使用 Writes.transform

import play.api.libs.json._

case class Personalization(firstname: String, lastname: String, keycardId: String) // No need to seal a case class

implicit def writes: Writes[Personalization] = {
  val tx: JsValue => JsValue = {
    case JsObject(fields) => Json.toJson(fields.map {
      case (k, v) => Json.obj("id" -> k, "value" -> v)
    })

    case jsUnexpected => jsUnexpected // doesn't happen with OWrites
  }

  Json.writes[Personalization].transform(tx)
}

可按如下方式进行测试

val personalization = Personalization(
  firstname = "First",
  lastname = "Last",
  keycardId = "Foo")

val jsonRepr = Json.toJson(personalization)
// => [{"id":"firstname","value":"First"},{"id":"lastname","value":"Last"},{"id":"keycardId","value":"Foo"}]

阅读有点棘手:

implicit def reads: Reads[Personalization] = {
  type Field = (String, Json.JsValueWrapper)

  val fieldReads = Reads.seq(Reads[Field] { js =>
    for {
      id <- (js \ "id").validate[String]
      v <- (js \ "value").validate[JsValue]
    } yield id -> v
  })

  val underlying = Json.reads[Personalization]

  Reads[Personalization] { js =>
    js.validate(fieldReads).flatMap { fields =>
      Json.obj(fields: _*).validate(underlying)
    }
  }
}

可按如下方式进行测试

Json.parse("""[
  {"id":"firstname","value":"First"},
  {"id":"lastname","value":"Last"},
  {"id":"keycardId","value":"Foo"}
]""").validate[Personalization]
// => JsSuccess(Personalization(First,Last,Foo),)

Note that is approach can be used for any case class format.