Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
32 changes: 32 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 35 additions & 0 deletions update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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]
}
}
62 changes: 62 additions & 0 deletions update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Loading