Question

I'm using Play framework 2.2.2. I'm trying to handle json request like this one

[
  {
    "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"
  }
]

The Metadata_ field are dynamic field meaning the user can send what ever he want (eg. Metadata_color, etc...) What is the best way to handle this?

Can I use Readers with deserialize it to case class? How can I do this? I guess the dynamic field will be Map[String, String], but how should I make the reader parse this?

Thanks

Was it helpful?

Solution

Something like this could work:

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")
  }
}

OTHER TIPS

We have faced the exact problem and solved it using a custom implementation. The solution is detailed here

Example:

Scala class

case class Person(name: String, age: String, customFields: Map[String,String])

Default Json representation of above class will be:

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

But what we wanted was:

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

This was not very straight forward. While this could be possible using play framework, we didn’t want to complicate things too much. Finally we found a way to do it by returning a Map[String, String] which represents each class (it’s fields & values) using reflection and handle the behavior for custom fields separately.

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

Use the covertToMap() to convert any case class to a Map and then convert this map to normal Json using jackson json4s.

val json = Serialization.write(covertToMap(person))

Complete source code is available here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top