2016-11-29 58 views
0

在我们的应用程序中,我们有非常复杂的对象转换为JSON并返回结构。到目前为止,格式化的大部分都是对称的(除了一些非常特殊的情况,甚至出于安全原因)。播放Scala JSON - 有条件地将字段添加到Writer中的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 = {...} 

但是,这会,这将是难以维持一个代码创造了巨大的复杂性。

什么是最干净的方式来实现类似的东西?

回答

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

这是真棒。去试试吧! –

+0

只是为了更新,这工作,这正是我需要的。谢谢。 –