From e632ae7401cdea6fc7025f15bad28259d60a3d2c Mon Sep 17 00:00:00 2001 From: John Date: Fri, 26 Jan 2018 16:36:07 +0800 Subject: [PATCH] =?UTF-8?q?gparser=E5=8A=9F=E8=83=BD=E5=AE=8C=E5=96=84?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/encoding/gjson/gjson.go | 208 ++++++++++++++--------------- g/encoding/gparser/gparser_test.go | 162 ++++++++++++++++++++++ geg/encoding/gparser.go | 11 +- 3 files changed, 265 insertions(+), 116 deletions(-) create mode 100644 g/encoding/gparser/gparser_test.go diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index 01c790112..ab23acb02 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -9,7 +9,6 @@ package gjson import ( "sync" - "errors" "strings" "strconv" "io/ioutil" @@ -188,10 +187,11 @@ func (j *Json) GetFloat64(pattern string) float64 { return gconv.Float64(j.Get(pattern)) } -// 根据pattern查找并设置数据,该方法内部逻辑比较复杂,主要的作用是层级检索及节点创建,叶子赋值 +// 根据pattern查找并设置数据 // 注意: // 1、写入的时候"."符号只能表示层级,不能使用带"."符号的键名; // 2、写入的value为nil时,表示删除; +// 3、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值; func (j *Json) Set(pattern string, value interface{}) error { // 初始化判断 if *j.p == nil { @@ -201,21 +201,22 @@ func (j *Json) Set(pattern string, value interface{}) error { *j.p = make(map[string]interface{}) } } - value = j.convertValue(value) - array := strings.Split(pattern, ".") - // root节点 - if len(array) == 1 { - return j.setRoot(pattern, value) - } + var pparent *interface{} + var pointer *interface{} + + pointer = j.p + pparent = nil + removed := false + value = j.convertValue(value) + array := strings.Split(pattern, ".") + length := len(array) + j.mu.Lock() - pointer := j.p - pparent := pointer - length := len(array) for i:= 0; i < length; i++ { switch (*pointer).(type) { case map[string]interface{}: if i == length - 1 { - if value == nil { + if removed && value == nil { // 删除map元素 delete((*pointer).(map[string]interface{}), array[i]) } else { @@ -225,15 +226,9 @@ func (j *Json) Set(pattern string, value interface{}) error { // 当键名不存在的情况这里会进行处理 v, ok := (*pointer).(map[string]interface{})[array[i]] if !ok { - if value == nil { + if removed && value == nil { goto done } - if strings.Compare(array[i + 1], "0") == 0 { - v = make([]interface{}, 0) - } else { - v = make(map[string]interface{}) - } - (*pointer).(map[string]interface{})[array[i]] = v } pparent = pointer pointer = &v @@ -244,26 +239,25 @@ func (j *Json) Set(pattern string, value interface{}) error { if n, err := strconv.Atoi(array[i]); err == nil { if i == length - 1 { if len((*pointer).([]interface{})) > n { - if value == nil { + if removed && value == nil { // 删除数据元素 - j.mu.Unlock() - return j.Set(strings.Join(array[0 : i], "."), append((*pointer).([]interface{})[ : n], (*pointer).([]interface{})[n + 1 : ]...)) + j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : n], (*pointer).([]interface{})[n + 1 : ]...)) } else { (*pointer).([]interface{})[n] = value } } else { - if value == nil { + if removed && value == nil { goto done } - // 注意这里产生了临时变量和赋值拷贝 + // 叶子节点:需要对父级重新赋值 s := make([]interface{}, n + 1) copy(s, (*pointer).([]interface{})) s[n] = value - if i == 0 { - *j.p = s + if pparent != nil { + pparent = j.setPointerWithValue(pparent, array[i - 1], s) } else { - j.mu.Unlock() - return j.Set(strings.Join(array[0 : i], "."), s) + *pointer = s + pparent = pointer } } } else { @@ -272,16 +266,19 @@ func (j *Json) Set(pattern string, value interface{}) error { pparent = pointer pointer = &(*pointer).([]interface{})[n] } else { - if value == nil { + if removed && value == nil { goto done } - var v interface{} - s := make([]interface{}, len((*pointer).([]interface{}))) + // 1.0 + s := make([]interface{}, n + 1) copy(s, (*pointer).([]interface{})) - j.setPointerWithValue(pparent, i, array[i], s) - v = s - pparent = pointer - pointer = &v + if pparent != nil { + pparent = j.setPointerWithValue(pparent, array[i - 1], s) + } else { + *pointer = s + pparent = pointer + } + pointer = &s[n] } } } else { @@ -289,38 +286,61 @@ func (j *Json) Set(pattern string, value interface{}) error { return err } } else { - j.mu.Unlock() - return errors.New("\"" + strings.Join(array[0:i], ".") + "\" is array, invalid index - \"" + array[i] + "\"") - } - - default: - if value == nil { - goto done - } - var v interface{} - // 判断当前节点应当为map或者数组 - if strings.Compare(array[i], "0") == 0 { - if i == length - 1 { - v = []interface{}{value} - } else { - if strings.Compare(array[i + 1], "0") == 0 { - v = make([]interface{}, 0) - } else { - v = make(map[string]interface{}) - } - } - } else { + var v interface{} if i == length - 1 { v = map[string]interface{}{ array[i] : value, } } else { - v = make(map[string]interface{}) + v = map[string]interface{}{} } + if pparent != nil { + pparent = j.setPointerWithValue(pparent, array[i], v) + } else { + *pointer = v + pparent = pointer + } + pointer = &v + } + + default: + if removed && value == nil { + goto done + } + // 判断当前节点应当为map或者数组 + if isNumeric(array[i]) { + if n, err := strconv.Atoi(array[i]); err == nil { + s := make([]interface{}, n + 1) + if i == length - 1 { + s[n] = value + } + if pparent != nil { + pparent = j.setPointerWithValue(pparent, array[i - 1], s) + } else { + *pointer = s + pparent = pointer + } + pointer = &s[n] + } else { + return err + } + } else { + var v interface{} + if i == length - 1 { + v = map[string]interface{}{ + array[i] : value, + } + } else { + v = map[string]interface{}{} + } + if pparent != nil { + pparent = j.setPointerWithValue(pparent, array[i - 1], v) + } else { + *pointer = v + pparent = pointer + } + pointer = &v } - j.setPointerWithValue(pparent, i, array[i - 1], v) - pparent = pointer - pointer = &v } } done: @@ -328,29 +348,6 @@ done: return nil } -// 用于Set方法中,对指针指向的内存地址进行赋值 -func (j *Json) setPointerWithValue(pointer *interface{}, index int, key string, value interface{}) { - if index == 0 { - *j.p = value - return - } - switch (*pointer).(type) { - case map[string]interface{}: - (*pointer).(map[string]interface{})[key] = value - case []interface{}: - n, _ := strconv.Atoi(key) - if len((*pointer).([]interface{})) > n { - (*pointer).([]interface{})[n] = value - } else { - s := make([]interface{}, n + 1) - copy(s, (*pointer).([]interface{})) - s[n] = value - *pointer = s - } - } - //fmt.Println("end:", *j.p) -} - // 数据结构转换,map参数必须转换为map[string]interface{},数组参数必须转换为[]interface{} func (j *Json) convertValue(value interface{}) interface{} { switch value.(type) { @@ -368,38 +365,27 @@ func (j *Json) convertValue(value interface{}) interface{} { return value } -// 修改根节点数据 -func (j *Json) setRoot(pattern string, value interface{}) error { - switch (*j.p).(type) { +// 用于Set方法中,对指针指向的内存地址进行赋值 +// 返回修改后的父级指针 +func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} { + switch (*pointer).(type) { case map[string]interface{}: - if value == nil { - delete((*j.p).(map[string]interface{}), pattern) - } else { - (*j.p).(map[string]interface{})[pattern] = value - } + (*pointer).(map[string]interface{})[key] = value + return &value case []interface{}: - if isNumeric(pattern) { - if n, err := strconv.Atoi(pattern); err != nil { - return err - } else { - if len((*j.p).([]interface{})) >= n { - if value == nil { - (*j.p) = append((*j.p).([]interface{})[ : n], (*j.p).([]interface{})[n + 1 : ]...) - } else { - (*j.p).([]interface{})[n] = value - } - } else { - if value != nil { - // 注意这里产生了临时变量和赋值拷贝 - array := (*j.p).([]interface{}) - array = append(array, value) - *j.p = array - } - } - } + n, _ := strconv.Atoi(key) + if len((*pointer).([]interface{})) > n { + (*pointer).([]interface{})[n] = value + return &(*pointer).([]interface{})[n] + } else { + s := make([]interface{}, n + 1) + copy(s, (*pointer).([]interface{})) + s[n] = value + *pointer = s + return &s[n] } } - return nil + return pointer } // 根据约定字符串方式访问json解析数据,参数形如: "items.name.first", "list.0" diff --git a/g/encoding/gparser/gparser_test.go b/g/encoding/gparser/gparser_test.go new file mode 100644 index 000000000..65bf3dc2d --- /dev/null +++ b/g/encoding/gparser/gparser_test.go @@ -0,0 +1,162 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. + +package gparser_test + +import ( + "bytes" + "testing" + "gitee.com/johng/gf/g/encoding/gparser" + "fmt" +) + +func Test_Set1(t *testing.T) { + e := []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`) + p := gparser.New(map[string]string{ + "k1" : "v1", + "k2" : "v2", + }) + p.Set("k1.k11", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set2(t *testing.T) { + e := []byte(`[[null,1]]`) + p := gparser.New([]string{"a"}) + p.Set("0.1", 1) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set3(t *testing.T) { + e := []byte(`{"kv":{"k1":"v1"}}`) + p := gparser.New([]string{"a"}) + p.Set("kv", map[string]string{ + "k1" : "v1", + }) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set4(t *testing.T) { + e := []byte(`["a",[{"k1":"v1"}]]`) + p := gparser.New([]string{"a"}) + p.Set("1.0", map[string]string{ + "k1" : "v1", + }) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set5(t *testing.T) { + e := []byte(`[[[[[[[[[[[[[[[[[[[[[1,2,3]]]]]]]]]]]]]]]]]]]]]`) + p := gparser.New([]string{"a"}) + p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set6(t *testing.T) { + e := []byte(`["a",[1,2,3]]`) + p := gparser.New([]string{"a"}) + p.Set("1", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set7(t *testing.T) { + e := []byte(`{"0":[null,[1,2,3]],"k1":"v1","k2":"v2"}`) + p := gparser.New(map[string]string{ + "k1" : "v1", + "k2" : "v2", + }) + p.Set("0.1", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set8(t *testing.T) { + e := []byte(`{"0":[[[[[[null,[1,2,3]]]]]]],"k1":"v1","k2":"v2"}`) + p := gparser.New(map[string]string{ + "k1" : "v1", + "k2" : "v2", + }) + p.Set("0.0.0.0.0.0.1", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + +func Test_Set9(t *testing.T) { + e := []byte(`{"k1":[null,[1,2,3]],"k2":"v2"}`) + p := gparser.New(map[string]string{ + "k1" : "v1", + "k2" : "v2", + }) + p.Set("k1.1", []int{1,2,3}) + if c, err := p.ToJson(); err == nil { + fmt.Println(string(c)) + if bytes.Compare(c, e) != 0 { + t.Error("expect:", string(e)) + } + } else { + t.Error(err) + } +} + + + + + + diff --git a/geg/encoding/gparser.go b/geg/encoding/gparser.go index 8b29e009f..d96a5fcd2 100644 --- a/geg/encoding/gparser.go +++ b/geg/encoding/gparser.go @@ -147,15 +147,16 @@ func makeJson2() { "k1" : "v1", "k2" : "v2", }) - p.Set("k1.k11", []int{1,2,3}) + p.Set("k1.1", []int{1,2,3}) + //p.Set("0.0.1", []int{1,2,3}) c, _ := p.ToJson() fmt.Println(string(c)) } func makeJson3() { p := gparser.New([]string{"a"}) - p.Set("1.1", 10) - c, _ := p.ToJsonIndent() + p.Set("0.0.0", []int{1,2,3}) + c, _ := p.ToJson() fmt.Println(string(c)) } @@ -186,6 +187,6 @@ func convert() { } func main() { - //makeJson2() - makeJson3() + makeJson2() + //makeJson3() } \ No newline at end of file