Search code examples
jsonscalacirce

Using Circe optics to modify all fields of an object, or all items of an array


I am trying to modify a nested JSON structure using Circe's optics. However, all the examples are only modifying a single field within the object with the known name.

What I need to do:

  • Assuming foo key of my object contains an array of objects, increment the counter key in each of those.
  • Assuming bar key of my object contains an object, increment the counter key in values mapped to each key in that object.
  • Keep all the other values in the object intact.

Example:

{
  "foo": [
    {
      "counter": 1
    },
    {
      "counter": 2
    }
  ],
  "bar": {
    "three": {
      "counter": 3
    },
    "four": {
      "counter": 4
    }
  }
}

should become

{
  "foo": [
    {
      "counter": 2
    },
    {
      "counter": 3
    }
  ],
  "bar": {
    "three": {
      "counter": 4
    },
    "four": {
      "counter": 5
    }
  }
}

The behavior when the types of the object and its members are not what I expect is not important.

I expect something like this:

val incrementCounterArray: Json => Json =
    root.foo.eachArrayItem.counter.modify(_ + 1)
val incrementCounterObject: Json => Json =
    root.bar.eachObjectValue.counter.modify(_ + 1)

but I don't see any definitions for what would be eachArrayItem or eachObjectValue in the tutorial.


Solution

  • The correct syntax is

    val incrementCounterArray: Json => Json =
      root.foo.each.counter.int.modify(_ + 1)
    
    val incrementCounterObject: Json => Json =
      root.bar.each.counter.int.modify(_ + 1)
    

    Have a look at the example in the official circe-optics documentation for more details: