如何使用Play的json序列化/反序列化动态字段名

4

我正在使用Play框架2.2.2。 我正在尝试处理像这样的JSON请求

[
  {
    "id" : "123",
    "language" : "en",
    "text" : "This is an example of a text",
    "Metadata_IP" : "192.168.20.34",
    "Metadata_date" : "2001-07-04T12:08:56.235-0700"
  },
  {
    "id" : "124",
    "language" : "en",
    "text" : "Some more text here",
    "Metadata_IP" : "192.168.20.31",
    "Metadata_date" : "2001-07-04T12:09:56.235-0700",
    "Metadata_name" : "someone"
  }
]

元数据字段是动态字段,意味着用户可以发送任何他想要的内容(例如:Metadata_color等)。

如何处理最好?

我可以使用读取器将其反序列化为案例类吗?如何做到这一点?我猜测动态字段将是Map[String, String],但是阅读器应该如何解析它?

谢谢

2个回答

7

这样的东西可能会有用:

implicit object jsObjToKeyValueSeq extends Reads[Seq[(String, String)]] {
  override def reads(json: JsValue) = json match {
    case js: JsObject => 
      JsSuccess(js.fields.collect { case (key, JsString(value)) => key -> value })
    case x => JsError(s"Unexpected json: $x")
  }
}

谢谢。它能否作为另一个读取器的一部分使用?我只想获取动态字段的地图,其他字段是固定的。我希望有以下列表:case class RequestData(id: String, language: String, text: String, Metadata: Map[String, String])。 - roterl
我错过了你没有将它们包装在子对象中,而是与手动读取的内容处于同一级别。我可能会选择以两个步骤的方式进行阅读,第一个步骤创建一个带有空元数据映射的RequestData,然后通过属性名称匹配或使用已读字段的黑名单来读取元数据。 - johanandren

-1
我们曾经遇到过同样的问题,并使用自定义实现解决了它。解决方案在这里详细说明。
示例:
Scala类
case class Person(name: String, age: String, customFields: Map[String,String])

上述类的默认Json表示如下:

{
 "name": "anil",
 "age": "30",
 "customFields": {
     "field1": "value1",
     "field2": "value2"
 }
}

但是我们想要的是:

{
 "name": "anil",
 "age": "30",
 "field1": "value1",
 "field2": "value2"
}

这并不是非常直接的。虽然使用Play框架可能是可行的,但我们不想让事情变得太复杂。最终,我们通过使用反射返回一个Map[String, String]来表示每个类(它的字段和值),并单独处理自定义字段的行为来找到了一种方法。
case class Person(name: String, age: String, customFields:CustomFields)

case class CustomFields(valueMap: Map[String,String])


def covertToMap(ref: AnyRef) =

  ref.getClass.getDeclaredFields.foldLeft(Map[String, Any]()){
    (map, field) => {
      field.setAccessible(true)
      val value = field.get(ref)
      value match {
        case c: CustomFields => {
          map ++ c.valueMap
        }
        case _ => {
          map + (field.getName -> value)
        }
      }
    }
  }

使用 covertToMap() 将任何 case class 转换为 Map,然后使用 jackson json4s 将此 map 转换为普通的 Json。
val json = Serialization.write(covertToMap(person))

完整的源代码可以在这里找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接