【问题标题】:Scala Play: List to Json-ArrayScala Play:列表到 Json-Array
【发布时间】:2021-02-25 08:54:01
【问题描述】:

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

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

我需要将此列表映射到一个 Json-Array 结构,该结构必须如下所示:

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

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

因此,我想请教一下,是否有可能使用 Scala Play-Framework? 非常感谢您的任何意见,谢谢!

【问题讨论】:

    标签: json scala play-json


    【解决方案1】:

    使用Writes.transform 编写这样的 JSON 表示并不复杂。

    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),)
    

    请注意,这种方法可用于任何案例类格式。

    【讨论】:

      【解决方案2】:

      也许可以用我做的更优雅的方式来做,但你可以使用下面的 sn-p:

      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),反之亦然

      【讨论】:

      • 不建议在Try 中抛出异常,特别是在可以使用JsResult 时。
      • .validate 应该优先使用.as
      猜你喜欢
      • 1970-01-01
      • 2013-11-20
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-12
      相关资源
      最近更新 更多