Given the following JSON...
{
\"metadata\": {
\"id\": \"1234\",
\"type\": \"file\",
\"length\": 395
}
}
... how do I convert it
@Trev has the best solution here, completely generic and recursive, but it's missing a case for array support. I'd like something that works in this scenario:
turn this:
{
"metadata": {
"id": "1234",
"type": "file",
"length": 395
},
"foo": "bar",
"person": {
"first": "peter",
"last": "smith",
"address": {
"city": "Ottawa",
"country": "Canada"
},
"kids": ["Bob", "Sam"]
}
}
into this:
{
"metadata.id": "1234",
"metadata.type": "file",
"metadata.length": 395,
"foo": "bar",
"person.first": "peter",
"person.last": "smith",
"person.address.city": "Ottawa",
"person.address.country": "Canada",
"person.kids[0]": "Bob",
"person.kids[1]": "Sam"
}
I've arrived at this, which appears to work, but seems overly verbose. Any help in making this pretty would be appreciated.
def flatten(js: JsValue, prefix: String = ""): JsObject = js.as[JsObject].fields.foldLeft(Json.obj()) {
case (acc, (k, v: JsObject)) => {
val nk = if(prefix.isEmpty) k else s"$prefix.$k"
acc.deepMerge(flatten(v, nk))
}
case (acc, (k, v: JsArray)) => {
val nk = if(prefix.isEmpty) k else s"$prefix.$k"
val arr = flattenArray(v, nk).foldLeft(Json.obj())(_++_)
acc.deepMerge(arr)
}
case (acc, (k, v)) => {
val nk = if(prefix.isEmpty) k else s"$prefix.$k"
acc + (nk -> v)
}
}
def flattenArray(a: JsArray, k: String = ""): Seq[JsObject] = {
flattenSeq(a.value.zipWithIndex.map {
case (o: JsObject, i: Int) =>
flatten(o, s"$k[$i]")
case (o: JsArray, i: Int) =>
flattenArray(o, s"$k[$i]")
case a =>
Json.obj(s"$k[${a._2}]" -> a._1)
})
}
def flattenSeq(s: Seq[Any], b: Seq[JsObject] = Seq()): Seq[JsObject] = {
s.foldLeft[Seq[JsObject]](b){
case (acc, v: JsObject) =>
acc:+v
case (acc, v: Seq[Any]) =>
flattenSeq(v, acc)
}
}