Skip to content

Commit bb56859

Browse files
author
Thomas Charlot
authoredJan 26, 2021
Better set json with jsonizer / *json (#11)
1 parent 0381de9 commit bb56859

File tree

4 files changed

+151
-67
lines changed

4 files changed

+151
-67
lines changed
 

‎go.sum

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
55
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
66
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
77
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
8-
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
9-
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
108
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
119
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
10+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1211
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1312
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
1413
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

‎json.go

+42-26
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ type Json struct {
1212
data interface{}
1313
}
1414

15+
// A Jsonizer can converts to a json
16+
type Jsonizer interface {
17+
JSON() *Json
18+
}
19+
1520
// New creates an empty object Json, ie {}
1621
func New() *Json {
1722
return &Json{
@@ -239,20 +244,46 @@ func (j *Json) Has(path string) bool {
239244

240245
// Set sets the value at path of object. If a portion of path doesn't exist, it's created.
241246
// Arrays are created for missing index properties while objects are created for all other missing properties
242-
func (j *Json) Set(path string, value ...interface{}) bool {
247+
func (j *Json) Set(path string, value interface{}) bool {
243248
keys := createPath(path)
244249
lastIndex := len(keys) - 1
245250

246251
// Pick value
247252
var newValue interface{}
248-
lv := len(value)
249-
250-
if lv == 0 {
251-
newValue = nil
252-
} else if lv == 1 {
253-
newValue = value[0]
254-
} else {
255-
newValue = value
253+
254+
switch cv := value.(type) {
255+
case Jsonizer:
256+
if jsub := cv.JSON(); jsub != nil {
257+
newValue = jsub.data
258+
}
259+
260+
case *Json:
261+
newValue = cv.data
262+
263+
case []*Json:
264+
datas := make([]interface{}, 0, len(cv))
265+
for _, item := range cv {
266+
datas = append(datas, item.data)
267+
}
268+
newValue = datas
269+
270+
case []interface{}:
271+
newValue = cv
272+
273+
default:
274+
rv := reflect.ValueOf(value)
275+
kind := rv.Kind()
276+
277+
if kind == reflect.Array || kind == reflect.Slice {
278+
l := rv.Len()
279+
datas := make([]interface{}, l)
280+
for i := 0; i < l; i++ {
281+
datas[i] = rv.Index(i).Interface()
282+
}
283+
newValue = datas
284+
} else {
285+
newValue = value
286+
}
256287
}
257288

258289
if lastIndex == -1 {
@@ -281,16 +312,10 @@ func (j *Json) Set(path string, value ...interface{}) bool {
281312
if a := curr.AsArray(); a != nil {
282313
// Must be an int
283314
idx, e := strconv.Atoi(k)
284-
if e != nil || idx < 0 {
315+
if e != nil || idx < 0 || idx >= len(a) {
285316
return false
286317
}
287318

288-
if idx >= len(a) {
289-
for q := len(a); q <= idx; q++ {
290-
a = append(a, nil)
291-
}
292-
}
293-
294319
// Assign value
295320
if i == lastIndex {
296321
a[idx] = newValue
@@ -318,15 +343,6 @@ func (j *Json) Set(path string, value ...interface{}) bool {
318343
return false
319344
}
320345

321-
// SetJSON to sets a json or an array of json to path
322-
func (j *Json) SetJSON(path string, json ...*Json) bool {
323-
var d []interface{}
324-
for _, o := range json {
325-
d = append(d, o.data)
326-
}
327-
return j.Set(path, d...)
328-
}
329-
330346
// Unset deletes the value
331347
func (j *Json) Unset(path string) bool {
332348
keys := createPath(path)
@@ -380,7 +396,7 @@ func (j *Json) Rewrite(oldPath string, newPath string) bool {
380396
// Returns the new parent or nilJson if error
381397
func (j *Json) Wrap(path string) *Json {
382398
wrap := New()
383-
if !wrap.Set(path, j.data) {
399+
if !wrap.Set(path, j) {
384400
return Nil()
385401
}
386402
return wrap

‎json_test.go

+108-34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package jsonmap_test
22

33
import (
44
"encoding/json"
5+
"fmt"
56
"sort"
67
"testing"
78

@@ -25,6 +26,19 @@ const jsonTest = `
2526
}
2627
`
2728

29+
type Person struct {
30+
FirstName string
31+
Name string
32+
Age int
33+
}
34+
35+
func (p *Person) JSON() *jsonmap.Json {
36+
j := jsonmap.New()
37+
j.Set("name", fmt.Sprintf("%s %s", p.FirstName, p.Name))
38+
j.Set("age", p.Age)
39+
return j
40+
}
41+
2842
func TestNewJson(t *testing.T) {
2943
j := jsonmap.New()
3044
assert.False(t, j.IsNil())
@@ -116,42 +130,102 @@ func TestGetPath(t *testing.T) {
116130
}
117131

118132
func TestSet(t *testing.T) {
119-
j := jsonmap.New()
120-
assert.True(t, j.Set("", 3.14))
121-
assert.Equal(t, 3.14, j.AsFloat())
122-
123-
// Collision test => try to sets object on value
124-
assert.True(t, j.Set("hello", "world"))
125-
assert.Equal(t, "world", j.Get("hello").AsString())
126-
127-
// Can reinject new value
128-
assert.True(t, j.Set("", "3.14"))
129-
assert.Equal(t, "3.14", j.AsString())
130-
131-
// Create auto map
132-
j = jsonmap.New()
133-
assert.True(t, j.Set("hello", "world"))
134-
assert.Equal(t, "world", j.Get("hello").AsString())
135-
assert.True(t, j.Set("the.number.pi.is", 3.14))
136-
assert.Equal(t, 3.14, j.Get("the.number.pi.is").AsFloat())
137-
138-
// Can sets array
139-
j = jsonmap.FromString(jsonTest)
140-
assert.Equal(t, int64(2), j.Get("array[1]").AsInt())
141-
assert.True(t, j.Set("array[1]", 3.14))
142-
assert.Equal(t, 3.14, j.Get("array[1]").AsFloat())
143-
assert.Equal(t, "b", j.Get("object.sub[1].1").AsString())
144-
assert.True(t, j.Set("object.sub[1].1", 3.14))
145-
assert.Equal(t, 3.14, j.Get("object.sub[1].1").AsFloat())
133+
t.Run("can set root path", func(t *testing.T) {
134+
j := jsonmap.New()
135+
assert.True(t, j.Set("", 3.14))
136+
assert.JSONEq(t, "3.14", j.Stringify())
137+
assert.Equal(t, 3.14, j.AsFloat())
138+
})
139+
140+
t.Run("can replace a value by an object", func(t *testing.T) {
141+
j := jsonmap.New()
142+
assert.True(t, j.Set("", 3.14))
143+
assert.True(t, j.Set("hello", "world"))
144+
assert.JSONEq(t, `{ "hello": "world" }`, j.Stringify())
145+
})
146+
147+
t.Run("can replace an object by a value", func(t *testing.T) {
148+
j := jsonmap.New()
149+
assert.True(t, j.Set("hello", "world"))
150+
assert.True(t, j.Set("", 3.14))
151+
assert.JSONEq(t, "3.14", j.Stringify())
152+
})
153+
154+
t.Run("can create sub path", func(t *testing.T) {
155+
j := jsonmap.New()
156+
assert.True(t, j.Set("hello", "world"))
157+
assert.True(t, j.Set("the.number.pi.is", 3.14))
158+
assert.JSONEq(t, `{ "hello": "world", "the": { "number": { "pi": { "is": 3.14 }}}}`, j.Stringify())
159+
})
160+
161+
t.Run("can create path with escape '.'", func(t *testing.T) {
162+
j := jsonmap.New()
163+
assert.True(t, j.Set("test\\.machin", 45))
164+
assert.True(t, j.Set("choux\\.machin.truc", "bidule"))
165+
assert.True(t, j.Set("choux.machin\\.truc", "bidule"))
166+
assert.JSONEq(t, `{ "test.machin": 45, "choux.machin": { "truc": "bidule" }, "choux": { "machin.truc": "bidule" }}`, j.Stringify())
167+
})
146168

147-
// can create auto array
148-
assert.True(t, j.Set("hello", 1, 2, 3, 4, 5))
149-
assert.Subset(t, []int{1, 2, 3, 4, 5}, j.Get("hello").AsArray())
169+
t.Run("can set array", func(t *testing.T) {
170+
j := jsonmap.New()
171+
assert.True(t, j.Set("items", []int{1, 2, 3, 4, 5}))
172+
assert.JSONEq(t, `{ "items": [1, 2, 3, 4, 5] }`, j.Stringify())
150173

151-
assert.True(t, j.Set("test\\.machin", 45))
152-
assert.Equal(t, int64(45), j.Get("test\\.machin").AsInt())
153-
assert.True(t, j.Set("choux\\.machin.truc", "bidule"))
154-
assert.Equal(t, "bidule", j.Get("choux\\.machin.truc").AsString())
174+
assert.True(t, j.Set("items[2]", 3.14))
175+
assert.JSONEq(t, `{ "items": [1, 2, 3.14, 4, 5] }`, j.Stringify())
176+
177+
assert.True(t, j.Set("items[3]", &Person{FirstName: "Thomas", Name: "CHARLOT", Age: 36}))
178+
assert.JSONEq(t, `{ "items": [1, 2, 3.14, {"name": "Thomas CHARLOT", "age": 36 }, 5] }`, j.Stringify())
179+
180+
assert.True(t, j.Set("items[3].age", 37))
181+
assert.JSONEq(t, `{ "items": [1, 2, 3.14, {"name": "Thomas CHARLOT", "age": 37 }, 5] }`, j.Stringify())
182+
183+
assert.False(t, j.Set("items[7]", 11))
184+
assert.JSONEq(t, `{ "items": [1, 2, 3.14, {"name": "Thomas CHARLOT", "age": 37 }, 5] }`, j.Stringify())
185+
})
186+
187+
t.Run("can set nil", func(t *testing.T) {
188+
j := jsonmap.New()
189+
assert.True(t, j.Set("nil", nil))
190+
assert.JSONEq(t, `{ "nil": null }`, j.Stringify())
191+
})
192+
193+
t.Run("can set a sub json", func(t *testing.T) {
194+
j := jsonmap.New()
195+
sub := jsonmap.New()
196+
assert.True(t, sub.Set("pi", 3.14))
197+
assert.True(t, j.Set("wrapped", sub))
198+
assert.JSONEq(t, `{ "wrapped": { "pi": 3.14 }}`, j.Stringify())
199+
})
200+
201+
t.Run("can set a json array", func(t *testing.T) {
202+
j := jsonmap.New()
203+
items := []*jsonmap.Json{
204+
jsonmap.FromString(`{ "string": "hello" }`),
205+
jsonmap.FromString(`{ "bool": true }`),
206+
jsonmap.FromString(`{ "number": 3.14 }`),
207+
jsonmap.FromString(`{ "array": [1,2,3,4,5] }`),
208+
jsonmap.FromString(`{ "object": { "a": 4, "1": "a" }}`),
209+
jsonmap.Nil(),
210+
}
211+
212+
assert.True(t, j.Set("items", items))
213+
assert.JSONEq(t,
214+
`{ "items": [{ "string": "hello" }, { "bool": true }, { "number": 3.14 }, { "array": [1,2,3,4,5] }, { "object": { "a": 4, "1": "a" }}, null ]}`,
215+
j.Stringify(),
216+
)
217+
})
218+
219+
t.Run("can set a jsonizer", func(t *testing.T) {
220+
j := jsonmap.New()
221+
person := &Person{FirstName: "Thomas", Name: "CHARLOT", Age: 36}
222+
assert.True(t, j.Set("person", person))
223+
assert.JSONEq(t,
224+
`{ "person": { "name": "Thomas CHARLOT", "age": 36 }}`,
225+
j.Stringify(),
226+
)
227+
228+
})
155229
}
156230

157231
func TestWrap(t *testing.T) {

‎jsonizer.go

-5
This file was deleted.

0 commit comments

Comments
 (0)