Use compasses to preprocess point style fields.

I have json that includes some fields that are flattened in bson-ish format, as in {"foo.bar" : "bash"}. I would like to transform this into the following representation {"foo" : { "bar" : "bash"}}and wonder where I would do such an operation. The complication of the problem is that there may be several such fields that need to be properly combined, for example. {"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}{"foo" : { "bar" : "a", "bash" : "b", "baz" : "c"}}.

+6
source share
1 answer

Here is a quick implementation:

import io.circe.Json

val Dotted = "([^\\.]*)\\.(.*)".r

def expandDotted(j: Json): Json = j.arrayOrObject(
  j,
  js => Json.fromValues(js.map(expandDotted)),
  _.toList.map {
    case (Dotted(k, rest), v) => Json.obj(k -> expandDotted(Json.obj(rest -> v)))
    case (k, v) => Json.obj(k -> expandDotted(v))
  }.reduceOption(_.deepMerge(_)).getOrElse(Json.obj())
)

I really haven't used or tested it in detail, but it seems to work:

scala> import io.circe.literal._
import io.circe.literal._

scala> val j1 = json"""{"foo.bar" : "a", "foo.bash" : "b", "foo.baz" : "c"}"""
j1: io.circe.Json =
{
  "foo.bar" : "a",
  "foo.bash" : "b",
  "foo.baz" : "c"
}

scala> expandDotted(j1)
res1: io.circe.Json =
{
  "foo" : {
    "baz" : "c",
    "bash" : "b",
    "bar" : "a"
  }
}

And with a deeper nesting:

scala> expandDotted(json"""{ "x.y.z": true, "a.b": { "c": 1 } }""")
res2: io.circe.Json =
{
  "a" : {
    "b" : {
      "c" : 1
    }
  },
  "x" : {
    "y" : {
      "z" : true
    }
  }
}

, :

scala> expandDotted(json"""{ "a.b": true, "x": 1 }""").noSpaces
res3: String = {"x":1,"a":{"b":true}}

, "" (, JSON, - JSON, - ), Json#deepMerge:

scala> expandDotted(json"""{ "a.b": true, "a": 1 }""").noSpaces
res4: String = {"a":1}

scala> expandDotted(json"""{ "a": 1, "a.b": true }""").noSpaces
res5: String = {"a":{"b":true}}

... , , , , , , .

+7

Source: https://habr.com/ru/post/1017369/


All Articles