播放Scala JSON - 有条件地将字段添加到Writes中的JSON对象

时间:2016-11-29 06:43:03

标签: json scala playframework

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

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

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可以保留为空,理想情况下甚至不应该存在)。

我明白我们必须创建单独的Reads&amp; amp;写给那个。 但是,我不确定,如何实现对不同类型的data做出不同反应的逻辑。

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

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

但是,这将在代码中产生巨大的复杂性,难以维护。

实现类似内容的最简洁方法是什么?

1 个答案:

答案 0 :(得分:2)

看看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!!!"
// }