jsonscalacircecirce-optics

Retrieving null values from json using circe-optics


The json I have looks like this:

{
    "cards": [
        {
            "card_id":"1234567890",
            "card_status":"active",
            "card_expiration":{
                "formatted":"01/20"
            },
            "debit":{
                "masked_debit_card_number":"1111 **** **** 1111",
            }
        },
        {
            "card_id":"1234567891",
            "card_status":"active",
            "card_expiration":null,
            "debit":{
                "masked_debit_card_number":"2222 **** **** 2222",
            }
        }
    ]
}

I'm trying to retrieve all card_expiration fields values using this function:

def getExpirations(json: Json) =
    root
        .cards
        .each
        .filter(root.card_status.string.exist(_ == "active"))
        .card_expiration
        .selectDynamic("formatted")
        .string
        .getAll(json)

The thing is, the above expression only returns 1 result - for the first card, but I really need to get something like List(Some("01/20"), None)! What can I do in this situation?


Solution

  • The issue is that by the time you've done the formatted step you're no longer matching the null expiration. You could do something like this:

    import io.circe.Json, io.circe.optics.JsonPath.root
    
    def getExpirations(json: Json) =
      root
        .cards
        .each
        .filter(root.card_status.string.exist(_ == "active"))
        .card_expiration
        .as[Option[Map[String, String]]]
        .getAll(json)
    

    Or:

    import io.circe.Json, io.circe.generic.auto._, io.circe.optics.JsonPath.root
    
    case class Expiration(formatted: String)
    
    def getExpirations(json: Json) =
      root
        .cards
        .each
        .filter(root.card_status.string.exist(_ == "active"))
        .card_expiration
        .as[Option[Expiration]]
        .getAll(json)
    

    And then:

    scala> getExpirations(io.circe.jawn.parse(doc).right.get)
    res0: List[Option[Expiration]] = List(Some(Expiration(01/20)), None)
    

    Without more context, it's not clear in my view that this is a good use case for circe-optics. You're probably better off decoding into case classes, or maybe using cursors. If you can provide more information it'd be easier to tell.