x25pads
x25pads

Reputation: 35

How to insert an empty object into JSON using Circe?

I'm getting a JSON object over the network, as a String. I'm then using Circe to parse it. I want to add a handful of fields to it, and then pass it on downstream.

Almost all of that works.

The problem is that my "adding" is really "overwriting". That's actually ok, as long as I add an empty object first. How can I add such an empty object?

So looking at the code below, I am overwriting "sometimes_empty:{}" and it works. But because sometimes_empty is not always empty, it results in some data loss. I'd like to add a field like: "custom:{}" and then ovewrite the value of custom with my existing code.

Two StackOverflow posts were helpful. One worked, but wasn't quite what I was looking for. The other I couldn't get to work.

1: Modifying a JSON array in Scala with circe

2: Adding field to a json using Circe

val js: String = """
{
  "id": "19",
  "type": "Party",
  "field": {
    "id": 1482,
    "name": "Anne Party",
    "url": "https"
  },
  "sometimes_empty": {

  },
  "bool": true,
  "timestamp": "2018-12-18T11:39:18Z"
}
"""

val newJson = parse(js).toOption
  .flatMap { doc =>
    doc.hcursor
      .downField("sometimes_empty")
      .withFocus(_ =>
        Json.fromFields(
          Seq(
            ("myUrl", Json.fromString(myUrl)),
            ("valueZ", Json.fromString(valueZ)),
            ("valueQ", Json.fromString(valueQ)),
            ("balloons", Json.fromString(balloons))
          )
        )
      )
      .top
  }

newJson match {
  case Some(v) => return v.toString
  case None => println("Failure!")
}

Upvotes: 1

Views: 3445

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

We need to do a couple of things. First, we need to zoom in on the specific property we want to update, if it doesn't exist, we'll create a new empty one. Then, we turn the zoomed in property in the form of a Json into JsonObject in order to be able to modify it using the +: method. Once we've done that, we need to take the updated property and re-introduce it in the original parsed JSON to get the complete result:

import io.circe.{Json, JsonObject, parser}
import io.circe.syntax._

object JsonTest {
  def main(args: Array[String]): Unit = {
    val js: String =
      """
        |{
        |  "id": "19",
        |  "type": "Party",
        |  "field": {
        |    "id": 1482,
        |    "name": "Anne Party",
        |    "url": "https"
        |  },
        |  "bool": true,
        |  "timestamp": "2018-12-18T11:39:18Z"
        |}
      """.stripMargin

    val maybeAppendedJson =
      for {
        json <- parser.parse(js).toOption
        sometimesEmpty <- json.hcursor
          .downField("sometimes_empty")
          .focus
          .orElse(Option(Json.fromJsonObject(JsonObject.empty)))
        jsonObject <- json.asObject
        emptyFieldJson <- sometimesEmpty.asObject
        appendedField = emptyFieldJson.+:("added", Json.fromBoolean(true))
        res = jsonObject.+:("sometimes_empty", appendedField.asJson)
      } yield res

    maybeAppendedJson.foreach(obj => println(obj.asJson.spaces2))
  }
}

Yields:

{
  "id" : "19",
  "type" : "Party",
  "field" : {
    "id" : 1482,
    "name" : "Anne Party",
    "url" : "https"
  },
  "sometimes_empty" : {
    "added" : true,
    "someProperty" : true
  },
  "bool" : true,
  "timestamp" : "2018-12-18T11:39:18Z"
}

Upvotes: 1

Related Questions