From 62892351c5f689619811f111e764b051a401ef2b Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Wed, 30 Nov 2016 10:50:59 -0700 Subject: [PATCH] added ForEach function --- README.md | 14 ++++++++++++ gjson.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ gjson_test.go | 37 +++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/README.md b/README.md index 276b05d..786afbe 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,20 @@ name := gjson.Get(json, `programmers.#[lastName="Hunter"].firstName`) println(name.String()) // prints "Elliotte" ``` +## Iterate through an object or array + +The `ForEach` function allows for quickly iterating through an object or array. +The key and value are passed to the iterator function for objects. +Only the value is passed for arrays. +Returning `false` from an iterator will stop iteration. + +```go +result := gjson.Get(json, "programmers") +result.ForEach(func(key, value Result) bool{ + println(value.String()) + return true // keep iterating +}) +``` ## Simple Parse and Get diff --git a/gjson.go b/gjson.go index 1f077fa..cd8dae6 100644 --- a/gjson.go +++ b/gjson.go @@ -152,6 +152,66 @@ func (t Result) Array() []Result { return r.a } +// ForEach iterates through values. +// If the result represents a non-existent value, then no values will be iterated. +// If the result is an Object, the iterator will pass the key and value of each item. +// If the result is an Array, the iterator will only pass the value of each item. +// If the result is not a JSON array or object, the iterator will pass back one value equal to the result. +func (t Result) ForEach(iterator func(key, value Result) bool) { + if !t.Exists() { + return + } + if t.Type != JSON { + iterator(Result{}, t) + return + } + json := t.Raw + var keys bool + var i int + var key, value Result + for ; i < len(json); i++ { + if json[i] == '{' { + i++ + key.Type = String + keys = true + break + } else if json[i] == '[' { + i++ + break + } + if json[i] > ' ' { + return + } + } + var str string + var vesc bool + var ok bool + for ; i < len(json); i++ { + if keys { + if json[i] != '"' { + continue + } + i, str, vesc, ok = parseString(json, i+1) + if !ok { + return + } + if vesc { + key.Str = unescape(str[1 : len(str)-1]) + } else { + key.Str = str[1 : len(str)-1] + } + key.Raw = str + } + i, value, ok = parseAny(json, i, true) + if !ok { + return + } + if !iterator(key, value) { + return + } + } +} + // Map returns back an map of values. The result should be a JSON array. func (t Result) Map() map[string]Result { if t.Type != JSON { diff --git a/gjson_test.go b/gjson_test.go index 466dd2e..be61517 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -163,6 +163,43 @@ func TestBasic(t *testing.T) { if mtok.String() != `["Brett","Elliotte"]` { t.Fatalf("expected %v, got %v", `["Brett","Elliotte"]`, mtok.String()) } + + mtok = get(basicJSON, `loggy.programmers`) + var count int + mtok.ForEach(func(key, value Result) bool { + if key.Exists() { + t.Fatalf("expected %v, got %v", false, key.Exists()) + } + count++ + if count == 3 { + return false + } + if count == 1 { + i := 0 + value.ForEach(func(key, value Result) bool { + switch i { + case 0: + if key.String() != "firstName" || value.String() != "Brett" { + t.Fatalf("expected %v/%v got %v/%v", "firstName", "Brett", key.String(), value.String()) + } + case 1: + if key.String() != "lastName" || value.String() != "McLaughlin" { + t.Fatalf("expected %v/%v got %v/%v", "lastName", "McLaughlin", key.String(), value.String()) + } + case 2: + if key.String() != "email" || value.String() != "aaaa" { + t.Fatalf("expected %v/%v got %v/%v", "email", "aaaa", key.String(), value.String()) + } + } + i++ + return true + }) + } + return true + }) + if count != 3 { + t.Fatalf("expected %v, got %v", 3, count) + } mtok = get(basicJSON, `loggy.programmers.#[age=101].firstName`) if mtok.String() != "1002.3" { t.Fatalf("expected %v, got %v", "1002.3", mtok.String())