Given the following JSON...
{
\"metadata\": {
\"id\": \"1234\",
\"type\": \"file\",
\"length\": 395
}
}
... how do I convert it
This is definitely not trivial, but possible by trying to flatten it recursively. I haven't tested this thoroughly, but it works with your example and some other basic one's I've come up with using arrays:
object JsFlattener {
def apply(js: JsValue): JsValue = flatten(js).foldLeft(JsObject(Nil))(_++_.as[JsObject])
def flatten(js: JsValue, prefix: String = ""): Seq[JsValue] = {
js.as[JsObject].fieldSet.toSeq.flatMap{ case (key, values) =>
values match {
case JsBoolean(x) => Seq(Json.obj(concat(prefix, key) -> x))
case JsNumber(x) => Seq(Json.obj(concat(prefix, key) -> x))
case JsString(x) => Seq(Json.obj(concat(prefix, key) -> x))
case JsArray(seq) => seq.zipWithIndex.flatMap{ case (x, i) => flatten(x, concat(prefix, key + s"[$i]")) }
case x: JsObject => flatten(x, concat(prefix, key))
case _ => Seq(Json.obj(concat(prefix, key) -> JsNull))
}
}
}
def concat(prefix: String, key: String): String = if(prefix.nonEmpty) s"$prefix.$key" else key
}
JsObject
has the fieldSet
method that returns a Set[(String, JsValue)]
, which I mapped, matched against the JsValue
subclass, and continued consuming recursively from there.
You can use this example by passing a JsValue
to apply
:
val json = Json.parse("""
{
"metadata": {
"id": "1234",
"type": "file",
"length": 395
}
}
"""
JsFlattener(json)
We'll leave it as an exercise to the reader to make the code more beautiful looking.