lift-json manipulation - adding in the right place
Asked Answered
V

2

7

consider the following json:

{
  "type":"A1",
  "system":{
    "path":"/example.org/FooBar",
    "lastModified":"2013-10-01T12:00:00Z"
  },
  "fields":{
    "foo1":["bar1"],
    "foo2":["bar2"],
    "foo3":["bar3"]
  }
}

now, using lift-json, i want to change this json into:

{
  "type":"A1",
  "system":{
    "path":"/example.org/FooBar",
    "lastModified":"2013-10-01T12:00:00Z"
  },
  "fields":{
    "foo1":["bar1"],
    "foo2":["bar2"],
    "foo3":["bar3"]
  },
  "injected":{
    "bar1":"foo1",
    "bar2":"foo2"
  }
}

so, i tried the following:

scala> val json = parse("""
     |{
     |  "type":"A1",
     |  "system":{
     |    "path":"/example.org/FooBar",
     |    "lastModified":"2013-10-01T12:00:00Z"
     |  },
     |  "fields":{
     |    "foo1":["bar1"],
     |    "foo2":["bar2"],
     |    "foo3":["bar3"]
     |  }
     |}""")

json: net.liftweb.json.JValue = JObject(List(JField(type,JString(A1)), JField(system,JObject(List(JField(path,JString(/example.org/FooBar)), JField(lastModified,JString(2013-10-01T12:00:00Z))))), JField(fields,JObject(List(JField(foo1,JArray(List(JString(bar1)))), JField(foo2,JArray(List(JString(bar2)))), JField(foo3,JArray(List(JString(bar3)))))))))

scala> json transform{case JObject(l) => JObject(l ::: List(JField("injected", ("bar1" -> "foo1") ~ ("bar2" -> "foo2"))))}
res0: net.liftweb.json.JsonAST.JValue = JObject(List(JField(type,JString(A1)), JField(system,JObject(List(JField(path,JString(/example.org/FooBar)), JField(lastModified,JString(2013-10-01T12:00:00Z)), JField(injected,JObject(List(JField(bar1,JString(foo1)), JField(bar2,JString(foo2)))))))), JField(fields,JObject(List(JField(foo1,JArray(List(JString(bar1)))), JField(foo2,JArray(List(JString(bar2)))), JField(foo3,JArray(List(JString(bar3)))), JField(injected,JObject(List(JField(bar1,JString(foo1)), JField(bar2,JString(foo2)))))))), JField(injected,JObject(List(JField(bar1,JString(foo1)), JField(bar2,JString(foo2)))))))

scala> Printer.pretty(render(res0))
res1: String = 
{
  "type":"A1",
  "system":{
    "path":"/example.org/FooBar",
    "lastModified":"2013-10-01T12:00:00Z",
    "injected":{
      "bar1":"foo1",
      "bar2":"foo2"
    }
  },
  "fields":{
    "foo1":["bar1"],
    "foo2":["bar2"],
    "foo3":["bar3"],
    "injected":{
      "bar1":"foo1",
      "bar2":"foo2"
    }
  },
  "injected":{
    "bar1":"foo1",
    "bar2":"foo2"
  }
}

and as you can see, the injected part, was added into fields & system as well. i just wanted to add it once under the root.

so, what am i doing wrong? and how can i transform the json into the right structure i need?

Vallation answered 1/10, 2013 at 11:52 Comment(0)
P
6

The problem you are having is that the partial function defined within transform is matched at all possible levels within the json structure. Since the "system" and "fields" components of the outer JObject are themselves JObjects, they match the partial function, and get transormed as well.

To get the result you are seeking, you will need to make the match more specific, eg:

json transform {case JObject(fields) if (fields contains JField("type", "A1")) =>
   JObject(l ::: ...

using some unique information about the outermost object (here, that it has "type" set to "A1").

Alternatively, lift json has a merge capability that I have not dealt with, but which might give what you want:

json merge JObject(JField("injected", ...) :: Nil)
Portiaportico answered 1/10, 2013 at 12:15 Comment(1)
thank you! merge worked great, and gave me exactly what i wanted.Vallation
D
4

If you only need to add a field at a single place, transform isn't the best tool—you can just use ~ instead:

val newJson = json match {
  case obj: JObject =>
    obj ~ ("injected" -> ("bar1" -> "foo1") ~ ("bar2" -> "foo2"))
  case _ => throw new RuntimeException("Did not receive a JSON object!")
}

If you know for a fact that you'll always parse an object, you could cast json to JObject and avoid the matching business here.

Delmore answered 1/10, 2013 at 12:20 Comment(1)
i don't always use parse, it was just for the demonstration.Vallation

© 2022 - 2024 — McMap. All rights reserved.