zio-json icon indicating copy to clipboard operation
zio-json copied to clipboard

Extraction of single values

Open fsvehla opened this issue 3 years ago • 5 comments

Ideally without decoding the whole structure, support cursor-like operations to extract single values.

fsvehla avatar Feb 19 '21 01:02 fsvehla

This seems like an essential feature, and I'm surprised that it hasn't gotten more attention.

A common use case is looking for one or two interesting fields (which may be nested) in an otherwise large JSON document. An example is OpenWeatherMap Current Weather API, which returns a structure like

{
  "coord": {
    "lon": 10.99,
    "lat": 44.34
  },
  "weather": [
    {
      "id": 501,
      "main": "Rain",
      "description": "moderate rain",
      "icon": "10d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 298.48,
    "feels_like": 298.74,
    "temp_min": 297.56,
    "temp_max": 300.05,
    "pressure": 1015,
    "humidity": 64,
    "sea_level": 1015,
    "grnd_level": 933
  },
  "visibility": 10000,
  "wind": {
    "speed": 0.62,
    "deg": 349,
    "gust": 1.18
  },
  "rain": {
    "1h": 3.16
  },
  "clouds": {
    "all": 100
  },
  "dt": 1661870592,
  "sys": {
    "type": 2,
    "id": 2075663,
    "country": "IT",
    "sunrise": 1661834187,
    "sunset": 1661882248
  },
  "timezone": 7200,
  "id": 3163858,
  "name": "Zocca",
  "cod": 200
}

If all we are looking for is main.temp, requiring the client to model this entire structure as a bunch of case classes (and potentially having to keep them up to date with any, even irrelevant, API changes) is very wasteful w.r.t. to developer time. Allowing for cursor-based decoding immensely helps with backwards-compatibility, as many API changes (especially structural ones) can be accounted for inside the custom Decoder without invalidating the client-side data model.

In the absence of cursor support, is there a recommended workaround for dealing with this problem?

Thank you!

bmironenko avatar Apr 04 '23 18:04 bmironenko

What you’ve asked for already is supported, but it will currently decode and allocate a complete AST for the input node.

See https://scastie.scala-lang.org/IWEQA9ogQoK2oQT73RlKEg

  • zio.json.ast.Json is a Json AST (string, number, object, array, …)
  • Json has decoders and encoders
  • Json#get accepts a JsonCursor

fsvehla avatar Apr 05 '23 08:04 fsvehla

Thank you @fsvehla, I really appreciate your time and a quick response! This does indeed address the use case.

bmironenko avatar Apr 05 '23 14:04 bmironenko

Thanks @fsvehla but how that will go to extract an optional field from Json. Do we have some getOpt operator ?

isarwar-img avatar Jun 27 '23 15:06 isarwar-img

After little effort found out like this we can pick optional field

val fields = for {
  ast     <- source.fromJson[Json]
  tempAst <- ast.get(JsonCursor.field("main").isObject.field("temp"))
  optionalField <- ast.get(JsonCursor.field("competition")) match {
    case Right(value) => value.as[Option[Int]]
    case _ => Right(None)
  }
  temp    <- tempAst.as[BigDecimal]
} yield (temp, optionalField)

fields match {
  case Right(temp, optField) => println(s"$temp and $optField")
  case _ => println("Nothing")
}

isarwar-img avatar Jun 27 '23 15:06 isarwar-img