tidwall / gjson

Get JSON values quickly - JSON parser for Go
MIT License
14.1k stars 846 forks source link

[Question] Multi-condition in same query #218

Closed iamshreeram closed 3 years ago

iamshreeram commented 3 years ago

Hi, My use case is to

  1. Make a GET call
  2. Do some condition checks on the payload
  3. Update specific value in the payload
  4. Send it back to the server

Below is the sample payload -

{
    "app": "gfgeeks",
    "prop": [
          {"region": 736,"set": true,"score": 72},
          {"region": 563,"set": true,"score": 333},
          {"region": 563,"set": false,"score": 333}
    ],
    "index" : "haskell"
}

I want to modify the payload to below based on 2 condition ( "region": 563 & "set": true )

{
    "app": "gfgeeks",
    "prop": [
          {"region": 736,"set": true,"score": 72},
          {"region": 563,"set": true,"score": 334},     // modified score to 334
          {"region": 563,"set": false,"score": 333}
    ],
    "index" : "haskell"
}

As part of this, I'm trying to get the map from list of map

  1. I want to pass variables in the gjson.Get instead of actual value
    regionid := 563
    fmt.Println(gjson.Get(json, "prop|#(region==regionid)#"))
  2. Can I do multiple condition for getting the value in same query. For example, I want to get the map from the list of map whose region is same as regionid and also set is true. Something like fmt.Println(gjson.Get(json, "prop|#(region=regionid)#|#(set=true)#"))
  3. Once I get the map, I would want to update the score with different value / variable and update that back to main payload
  4. Also, When I get prop.#.region I'm unable to convert the Result interface to integer with Result.Int(). Below is the error
    varianame.Int undefined (type interface{} has no field or method Int)

Note: The props list in this example is small. But, real world list has multiple elements. If above is a expensive approach, Any suggestion to get this smarter is much appreciated. Thanks!

cc: @tidwall

tidwall commented 3 years ago

There's currently no way to do all that with a single query.

Perhaps a combination of gjson and sjson.

package main

import (
    "fmt"

    "github.com/tidwall/gjson"
    "github.com/tidwall/sjson"
)

func main() {
    json := `
{
    "app": "gfgeeks",
    "prop": [
          {"region": 736,"set": true,"score": 72},
          {"region": 563,"set": true,"score": 333},
          {"region": 563,"set": false,"score": 333}
    ],
    "index" : "haskell"
}`

    // loop through the "prop" values and find the target
    var index int
    var found bool
    gjson.Get(json, "prop").ForEach(func(_, value gjson.Result) bool {
        if value.Get("region").Int() == 563 && value.Get("set").Bool() {
            found = true
            return false
        }
        index++
        return true
    })
    if found {
        // if found the use sjson to update the value at index
        json, _ = sjson.Set(json, fmt.Sprintf("prop.%d.score", index), 334)
    }
    println(json)
}

Outputs:

{
    "app": "gfgeeks",
    "prop": [
          {"region": 736,"set": true,"score": 72},
          {"region": 563,"set": true,"score": 334},     // modified score to 334
          {"region": 563,"set": false,"score": 333}
    ],
    "index" : "haskell"
}
iamshreeram commented 3 years ago

Perfect. Thanks!