diff --git a/parser.go b/parser.go index 4a8193c..8e9ecd5 100644 --- a/parser.go +++ b/parser.go @@ -634,6 +634,25 @@ func (o *Object) Visit(f func(key []byte, v *Value)) { } } +// ReverseVisit calls f for each item in the o in reverse of the original order +// of the parsed JSON. f should return true to keep iterating or false to stop. +// +// f cannot hold key and/or v after returning. +func (o *Object) ReverseVisit(f func(key []byte, v *Value) bool) { + if o == nil { + return + } + + o.unescapeKeys() + + for i := len(o.kvs) - 1; i >= 0; i-- { + kv := &o.kvs[i] + if !f(s2b(kv.k), kv.v) { + break + } + } +} + // Value represents any JSON value. // // Call Type in order to determine the actual type of the JSON value. diff --git a/parser_test.go b/parser_test.go index 85c265d..66582bf 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1251,6 +1251,38 @@ func TestParserParse(t *testing.T) { }) } +func TestReverseVisit(t *testing.T) { + var p Parser + v, err := p.Parse(citmFixture) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + o, err := v.Object() + if err != nil { + t.Fatalf("cannot obtain object: %s", err) + } + var arena Arena + var expectedKeys []string + var expectedValues []*Value + o.Visit(func(k []byte, v *Value) { + expectedKeys = append(expectedKeys, string(k)) + expectedValues = append(expectedValues, arena.DeepCopyValue(v)) + }) + i := len(expectedKeys) - 1 + o.ReverseVisit(func(key []byte, v *Value) bool { + if string(key) != expectedKeys[i] { + t.Fatalf("unexpected key at index %d; got %s; want %s", i, string(key), expectedKeys[i]) + } + expectedValueStr := expectedValues[i].String() + actualValueStr := v.String() + if actualValueStr != expectedValueStr { + t.Fatalf("unexpected value at index %d; got %s; want %s", i, actualValueStr, expectedValueStr) + } + i-- + return true + }) +} + func TestParseBigObject(t *testing.T) { const itemsCount = 10000 diff --git a/update.go b/update.go index f8099bd..17f2612 100644 --- a/update.go +++ b/update.go @@ -31,6 +31,27 @@ func (o *Object) Del(key string) { } } +// DelMany deletes all entries from o who are present in the given keysToDelete set. +// Returns the number of keys that were deleted +func (o *Object) DelMany(keysToDelete map[string]struct{}) int { + if o == nil { + return 0 + } + o.unescapeKeys() + numDeleted := 0 + nextUsedIndex := 0 + for _, kv := range o.kvs { + if _, exists := keysToDelete[kv.k]; !exists { + o.kvs[nextUsedIndex] = kv + nextUsedIndex++ + } else { + numDeleted++ + } + } + o.kvs = o.kvs[:nextUsedIndex] + return numDeleted +} + // Del deletes the entry with the given key from array or object v. func (v *Value) Del(key string) { if v == nil { @@ -108,3 +129,17 @@ func (v *Value) SetArrayItem(idx int, value *Value) { } v.a[idx] = value } + +// SetArrayLength lengthens or shortens (cuts off the end) of the +// array v to the given length. +func (v *Value) SetArrayLength(length int) { + if v == nil || v.t != TypeArray { + return + } + for length > len(v.a) { + v.a = append(v.a, valueNull) + } + if len(v.a) > length { + v.a = v.a[:length] + } +} diff --git a/update_test.go b/update_test.go index 845e9f0..685d6b9 100644 --- a/update_test.go +++ b/update_test.go @@ -101,3 +101,65 @@ func TestValueDelSet(t *testing.T) { v.Set("x", MustParse(`[]`)) v.SetArrayItem(1, MustParse(`[]`)) } + +func TestObjectDelMany(t *testing.T) { + var p Parser + var o *Object + + o.Del("xx") + + v, err := p.Parse(`{"fo\no": "bar", "x": [1,2,3], "a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "a": "duplicate_key"}`) + if err != nil { + t.Fatalf("unexpected error during parse: %s", err) + } + o, err = v.Object() + if err != nil { + t.Fatalf("cannot obtain object: %s", err) + } + + keysToDelete := map[string]struct{}{ + "fo\no": {}, + "a": {}, + "c": {}, + "does_not_exist": {}, + } + numDeleted := o.DelMany(keysToDelete) + if numDeleted != 4 { + t.Fatalf("unexpected number of deleted items; got %d; want %d", numDeleted, 4) + } + + str := o.String() + strExpected := `{"x":[1,2,3],"b":2,"d":4,"e":5}` + if str != strExpected { + t.Fatalf("unexpected string representation for o: got %q; want %q", str, strExpected) + } + + o = nil + o.DelMany(keysToDelete) +} + +func TestSetArrayLength(t *testing.T) { + var p Parser + v, err := p.Parse(`{"x": [1, 2, 3]}`) + if err != nil { + t.Fatalf("unexpected error during parse: %s", err) + } + + va := v.Get("x") + va.SetArrayLength(5) + str := v.String() + strExpected := `{"x":[1,2,3,null,null]}` + if str != strExpected { + t.Fatalf("unexpected string representation after lengthening: got %q; want %q", str, strExpected) + } + + va.SetArrayLength(2) + str = v.String() + strExpected = `{"x":[1,2]}` + if str != strExpected { + t.Fatalf("unexpected string representation after shortening: got %q; want %q", str, strExpected) + } + + va = nil + va.SetArrayLength(10) +}