tidwall / gjson

Get JSON values quickly - JSON parser for Go
MIT License
13.95k stars 841 forks source link

Most efficient way to access nested fields in already accessed object? #272

Closed bmoe24x closed 2 years ago

bmoe24x commented 2 years ago

Hi, I appreciate this library a lot, nicely done. I am newish to Go and was wondering if there is a prescribed way to achieve what I am looking to do:

Say we have the following json (from the examples) in a variable called myJson:

{
  "friends": [
    {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
    {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
    {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  ]
}

If I need to loop over the friends array I can do something like this (not sure this is the best way either):

gjson.Get( myJson, "friends" ).ForEach( func( _, value gjson.Result ) bool {
    ... do some stuff ...
    return true // keep iterating
})

But what if I want to now do some actions with some of the fields like friends.last and friends.age? What is the best/most common way of doing this, particularly if I have a lot of child fields? Is it something like this for any field I want to access?

gjson.Get( myJson, "friends" ).ForEach( func( _, value gjson.Result ) bool {
    ... do some stuff ...

    lastName := gjson.Get( value.Raw, "last" ).Value().(string)
    age := gjson.Get( value.Raw, "age" ).Value().(int)

    ... do some stuff with lastName and age ...

    return true // keep iterating
})

Or is there a better way to cleanly access fields assuming I know at least a subset of the fields that will almost always be present?

Appreciate any recommendations

tidwall commented 2 years ago

All gjson.Result have methods for converting to strings, numbers, bools and such using methods like String() and Int(). They also have the Get(path) method that allows for retrieving child values.

gjson.Get( myJson, "friends" ).ForEach( func( _, value gjson.Result ) bool {

    lastName := value.Get("last").String()
    age := value.Get("age").Int()

    return true
})
tidwall commented 2 years ago

The Value() method returns an interface{} that contains a Go string, float64, bool, map of values, array or values, or nil.

While this may be useful for some more advance purposes, like sharing that value with other tools, or using custom encoders on the value. It's not flexible when you just want to get data out of a Result type because it requires type assertion using the Value().(type) pattern.

Using methods like Int(), String(), Bool() will automatically convert the Result to it's logical representation such as a Result from the json string "123.10" will be converted to its numerical representation using Float() or Int().