【问题标题】:Play Scala JSON - conditionally add field to JSON object in Writes播放 Scala JSON - 有条件地将字段添加到写入中的 JSON 对象
【发布时间】:2017-04-13 01:31:38
【问题描述】:

在我们的应用程序中,我们有相当复杂的对象结构,这些对象被转换为 JSON 并返回。到目前为止,大多数格式都是对称的(除了一些非常特殊的情况,甚至出于安全原因)。

现在我们面临一个更复杂的情况,将对象转换为 JSON(写入)需要在转换时创建一个额外的字段,而案例类没有该字段。 例如,这是我们现有的格式化程序之一:

case class ChecklistColumn(kind: ColumnKind.Value, descriptor: Descriptor.Value, data: JsValue) extends Column

implicit val checklistResultChecklistDataFormat: Format[ChecklistColumn] = (
  (__ \ "kind").format[ColumnKind.Value] and
  (__ \ "descriptor").format[Descriptor.Value] and
  (__ \ "data").format[JsValue]
)(ChecklistColumn.apply, unlift(ChecklistColumn.unapply))

这会创建一个 json,如下所示:

{
  "kind": <String>,
  "descriptor": <String>,
  "data": <JsValue>
}

我们需要实现的是:

{
  "kind": <String>,
  "descriptor": <String>,
  "data": <JsValue>,
  "normalized_data": <JsString>
}

但是,只有在数据类型为JsString 的情况下(在任何其他情况下,normalized_data 可以留空,理想情况下甚至不应该存在字节)。

我明白我们必须为此创建单独的读取和写入。 但是,我不确定如何实现对不同类型的data 做出不同反应的逻辑。

当然,始终可以选择创建完全自定义的writes

override def writes(column: ChecklistColumn): JsValue = {...}

但是,这会使代码变得非常复杂,难以维护。

实现这样的东西最干净的方法是什么?

【问题讨论】:

    标签: json scala playframework


    【解决方案1】:

    看看ScalaJsonTransformers。您可以创建一个转换器,从字符串数据值创建规范化字段,并使用它来将您的原始Format 转换为新的Writes。这是一个稍微简化的示例,毫无疑问可以改进(您需要检查各种边缘情况):

    case class ChecklistColumn(kind: String, descriptor: String, data: JsValue)
    
    // The original format.
    val checklistFormat: Format[ChecklistColumn] = (
      (__ \ "kind").format[String] and
      (__ \ "descriptor").format[String] and
      (__ \ "data").format[JsValue]
    )(ChecklistColumn.apply, unlift(ChecklistColumn.unapply))
    
    // A transformer that looks for a "data" field with a string
    // value and adds the normalized_data field if it finds one.
    val checklistTransformer: Reads[JsObject] = JsPath.json.update(
      (__ \ "data").read[String].flatMap (
         str => (__ \ "normalized_data").json.put(JsString(str + "!!!"))))
    
    // A new derived Writes which writes the transformed value if
    // the transformer succeeds (a data string), otherwise the
    // original value.
    implicit val checklistWrites: Writes[ChecklistColumn] = checklistFormat
      .transform (js => js.transform(checklistTransformer).getOrElse(js))
    

    这给了我:

    Json.prettyPrint(Json.toJson(ChecklistColumn("a", "b", JsNumber(1))))
    // {
    //   "kind" : "a",
    //   "descriptor" : "b",
    //   "data" : 1
    // }
    
    Json.prettyPrint(Json.toJson(ChecklistColumn("a", "b", JsString("c"))))
    // {
    //   "kind" : "a",
    //   "descriptor" : "b",
    //   "data" : "c",
    //   "normalized_data" : "c!!!"
    // }
    

    【讨论】:

    • 太棒了。去试试吧!
    • 只是为了更新,这行得通,这正是我所需要的。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 2016-09-28
    • 1970-01-01
    • 2021-11-12
    • 1970-01-01
    相关资源
    最近更新 更多