Search code examples
jsonscalalensesargonaut

How to modify a numeric JSON field using an Argonaut lens?


Here's a simplified version of an example on Argonaut's lens documentation:

import argonaut._, Argonaut._

val obj: Json = Parse.parseOption("""{"a":{"b":"x","c":2}}""").get

val lens1 = jObjectPL >=> jsonObjectPL("a") >=>
            jObjectPL >=> jsonObjectPL("b") >=> jStringPL

lens1.mod(_ + "y", obj)
// res0: argonaut.Json = {"a":{"b":"xy","c":2}}

How do you do a similar thing with numeric fields? For example, suppose we wanted to add 1 to the number in obj above, to get a result of

// res1: argonaut.Json = {"a":{"b":"x","c":3}}

My first attempt is to try

val lens2 = jObjectPL >=> jsonObjectPL("a") >=>
            jObjectPL >=> jsonObjectPL("c") >=> jNumberPL

lens2.mod(_ + 1, obj)

But this last line doesn't compile.


Solution

  • JsonString in Argonaut is simply a type alias for String, but numbers are slightly more complicated because there is no core Scala type that can perfectly represent numbers in JSON.

    The numeric type in Argonaut is JsonNumber, so the type of the first argument passed to mod needs to be (JsonNumber) => JsonNumber.

    The JsonNumber subtypes with public constructors are:

    • JsonLong
    • JsonBigDecimal
    • JsonDouble

    Here's a corrected version of the last line of code in the question, using JsonBigDecimal:

    lens2.mod(i => JsonBigDecimal(i.toBigDecimal + 1), obj)