gjson包改进,功能修复中

This commit is contained in:
John
2018-04-12 14:09:33 +08:00
parent 9d7bfa5b6d
commit 7b9813ee76
7 changed files with 364 additions and 99 deletions

34
g/container/gtype/byte.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2018 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 gtype
import (
"sync/atomic"
)
type Byte struct {
val int32
}
func NewByte(value...byte) *Byte {
if len(value) > 0 {
return &Byte{val : int32(value[0])}
}
return &Byte{}
}
func (t *Byte)Set(value byte) {
atomic.StoreInt32(&t.val, int32(value))
}
func (t *Byte)Val() byte {
return byte(atomic.LoadInt32(&t.val))
}
func (t *Byte)Add(delta int) byte {
return byte(atomic.AddInt32(&t.val, int32(delta)))
}

View File

@ -4,7 +4,8 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// JSON解析/封装
// JSON解析/封装.
// 单元测试请参考gpaser包.
package gjson
import (
@ -20,12 +21,58 @@ import (
"gitee.com/johng/gf/g/encoding/gtoml"
)
const (
gDEFAULT_SPLIT_CHAR = '.' // 默认层级分隔符号
)
// json解析结果存放数组
type Json struct {
mu sync.RWMutex
p *interface{} // 注意这是一个指针
c byte // 层级分隔符,默认为"."
vc bool // 是否执行分隔符冲突检测(默认为true检测会比较影响检索效率)
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者array否者转换没有意义
func New(value interface{}) *Json {
switch value.(type) {
case map[string]interface{}:
return &Json{
p : &value,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true ,
}
case []interface{}:
return &Json{
p : &value,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true ,
}
default:
// 这里效率会比较低
b, _ := Encode(value)
v, _ := Decode(b)
return &Json{
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : true,
}
}
}
// 设置自定义的层级分隔符号
func (j *Json) SetSplitChar(char byte) {
j.mu.Lock()
j.c = char
j.mu.Unlock()
}
// 设置自定义的层级分隔符号
func (j *Json) SetViolenceCheck(check bool) {
j.mu.Lock()
j.vc = check
j.mu.Unlock()
}
// 编码go变量为json字符串并返回json字符串指针
func Encode (v interface{}) ([]byte, error) {
return json.Marshal(v)
@ -51,7 +98,7 @@ func DecodeToJson (b []byte) (*Json, error) {
if v, err := Decode(b); err != nil {
return nil, err
} else {
return NewJson(v), nil
return New(v), nil
}
}
@ -94,26 +141,11 @@ func LoadContent (data []byte, t string) (*Json, error) {
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
}
return NewJson(result), nil
}
// 将变量转换为Json对象进行处理该变量至少应当是一个map或者array否者转换没有意义
func NewJson(value interface{}) *Json {
switch value.(type) {
case map[string]interface{}:
return &Json{ p: &value }
case []interface{}:
return &Json{ p: &value }
default:
// 这里效率会比较低
b, _ := Encode(value)
v, _ := Decode(b)
return &Json{ p: &v }
}
return New(result), nil
}
// 将指定的json内容转换为指定结构返回查找失败或者转换失败目标对象转换为nil
// 注意第二个参数需要给的是变量地址
// 注意第二个参数需要给的是**变量地址**
func (j *Json) GetToVar(pattern string, v interface{}) error {
r := j.Get(pattern)
if r != nil {
@ -144,7 +176,7 @@ func (j *Json) GetMap(pattern string) map[string]interface{} {
func (j *Json) GetJson(pattern string) *Json {
result := j.Get(pattern)
if result != nil {
return NewJson(result)
return New(result)
}
return nil
}
@ -199,27 +231,22 @@ func (j *Json) Remove(pattern string) error {
// 根据pattern查找并设置数据
// 注意:
// 1、写入的时候"."符号只能表示层级,不能使用带"."符号的键名;
// 2、写入的value为nil且removed为true时表示删除;
// 3、里面的层级处理比较复杂逻辑较复杂的地方在于层级检索及节点创建叶子赋值;
// 1、写入的value为nil且removed为true时表示删除;
// 2、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
array := strings.Split(pattern, string(j.c))
length := len(array)
value = j.convertValue(value)
// 初始化判断
if *j.p == nil {
if isNumeric(pattern) {
if isNumeric(array[0]) {
*j.p = make([]interface{}, 0)
} else {
*j.p = make(map[string]interface{})
}
}
var pparent *interface{}
var pointer *interface{}
pointer = j.p
pparent = nil
value = j.convertValue(value)
array := strings.Split(pattern, ".")
length := len(array)
var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级)
var pointer *interface{} = j.p // 当前操作层级项
j.mu.Lock()
for i:= 0; i < length; i++ {
switch (*pointer).(type) {
@ -403,13 +430,31 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
func (j *Json) Get(pattern string) interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
if r := j.getPointerByPattern(pattern); r != nil {
return *r
var result *interface{}
if j.vc {
result = j.getPointerByPattern(pattern)
} else {
result = j.getPointerByPatternWithoutSplitCharViolenceCheck(pattern)
}
if result != nil {
return *result
}
return nil
}
// 根据pattern层级查找变量指针
// 根据pattern层级查找**变量指针**
// 检索方式:例如检索 a.a.a 值为1
// 1. 检索 a.a.a.a 是否存在对应map的键名
// 2. 检索 a.a.a 是否存在对应map的键名
// 3. 检索 a.a 是否存在对应map的键名
// 4. 检索 a 是否存在对应map的键名如果检索出这是一个map假如为变量m1
// 5. 在m1中检索 a.a.a 否存在对应map的键名
// 6. 在m1中检索 a.a 否存在对应map的键名
// 7. 在m1中检索 a 否存在对应map的键名如果检索出这是一个map假如为变量m2
// 8. 在m2中检索 a.a 否存在对应map的键名
// 9. 在m2中检索 a 否存在对应map的键名检索到有值值为1
// 这样检索的复杂度很高,主要是为了避免键名中存在分隔符号(默认为".")的情况,避免歧义。
func (j *Json) getPointerByPattern(pattern string) *interface{} {
index := len(pattern)
start := 0
@ -432,7 +477,8 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
pointer = r
}
} else {
index = strings.LastIndex(pattern[start:index], ".")
// 查找下一个分割符号的索引位置
index = strings.LastIndexByte(pattern[start:index], j.c)
if index != -1 && length > 0 {
index += length + 1
}
@ -444,17 +490,38 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
return nil
}
// 判断给定的pattern在当前的pointer下是否有值并返回对应的pointer
// 层级检索,内部不执行分隔符冲突检查,检索效率会有所提高,但是冲突需要开发者自己根据自定义的分隔符来进行解决
func (j *Json) getPointerByPatternWithoutSplitCharViolenceCheck(pattern string) *interface{} {
pointer := j.p
if len(pattern) == 0 {
return pointer
}
array := strings.Split(pattern, string(j.c))
for k, v := range array {
if r := j.checkPatternByPointer(v, pointer); r != nil {
if k == len(array) - 1 {
return r
} else {
pointer = r
}
} else {
break
}
}
return nil
}
// 判断给定的key在当前的pointer下是否有值并返回对应的pointer
// 注意这里返回的指针都是临时变量的内存地址
func (j *Json) checkPatternByPointer(pattern string, pointer *interface{}) *interface{} {
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
switch (*pointer).(type) {
case map[string]interface{}:
if v, ok := (*pointer).(map[string]interface{})[pattern]; ok {
if v, ok := (*pointer).(map[string]interface{})[key]; ok {
return &v
}
case []interface{}:
if isNumeric(pattern) {
n, err := strconv.Atoi(pattern)
if isNumeric(key) {
n, err := strconv.Atoi(key)
if err == nil && len((*pointer).([]interface{})) > n {
return &(*pointer).([]interface{})[n]
}

View File

@ -20,9 +20,9 @@ type Parser struct {
// 该参数为非必需参数默认为创建一个空的Parser对象
func New (values...interface{}) *Parser {
if len(values) > 0 {
return &Parser{gjson.NewJson(values[0])}
return &Parser{gjson.New(values[0])}
}
return &Parser{gjson.NewJson(nil)}
return &Parser{gjson.New(nil)}
}
func Load (path string) (*Parser, error) {

View File

@ -156,6 +156,19 @@ func Test_Set9(t *testing.T) {
}
func Test_Set10(t *testing.T) {
e := []byte(`{"a":{"b":{"c":1}}`)
p := gparser.New(nil)
p.Set("a.b.c", 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)
}
}

View File

@ -25,8 +25,7 @@ const (
gHTTP_METHODS = "GET,POST,DELETE,PUT,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
gDEFAULT_SERVER = "default"
gDEFAULT_DOMAIN = "default"
gDEFAULT_METHOD = "all"
gDEFAULT_METHOD = "ALL"
gDEFAULT_COOKIE_PATH = "/" // 默认path
gDEFAULT_COOKIE_MAX_AGE = 86400*365 // 默认cookie有效期(一年)
gDEFAULT_SESSION_MAX_AGE = 600 // 默认session有效期(600秒)
@ -35,15 +34,17 @@ const (
// http server结构体
type Server struct {
hmu sync.RWMutex // handlerMap互斥锁
hmmu sync.RWMutex // handlerMap互斥锁
htmu sync.RWMutex // handlerTree互斥锁
name string // 服务名称,方便识别
server http.Server // 底层http server对象
config ServerConfig // 配置对象
status int8 // 当前服务器状态(0未启动1运行中)
handlerMap HandlerMap // 所有注册的回调函数
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
handlerMap HandlerMap // 所有注册的回调函数(静态匹配)
handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配)
hooksMap *gmap.StringInterfaceMap // 钩子注册方法map键值为按照注册顺序生成的glist用于hook顺序调用
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
servedCount *gtype.Int // 已经服务的请求数(4-8字节不考虑溢出情况)
cookieMaxAge *gtype.Int // Cookie有效期
sessionMaxAge *gtype.Int // Session有效期
@ -54,13 +55,17 @@ type Server struct {
}
// 域名、URI与回调函数的绑定记录表
type HandlerMap map[string]HandlerItem
type HandlerMap map[string]*HandlerItem
// http回调函数注册信息
type HandlerItem struct {
ctype reflect.Type // 控制器类型
fname string // 回调方法名称
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
ctype reflect.Type // 控制器类型
fname string // 回调方法名称
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
uri string // 注册时的pattern - uri
method string // 注册时的pattern - method
domain string // 注册时的pattern - domain
priority int // 优先级,用于链表排序,值越大优先级越高
}
// http注册函数
@ -81,11 +86,12 @@ func GetServer(names...string) (*Server) {
}
s := &Server {
name : name,
handlerMap : make(HandlerMap),
methodsMap : make(map[string]bool),
handlerMap : make(HandlerMap),
handlerTree : make(map[string]interface{}),
hooksMap : gmap.NewStringInterfaceMap(),
servedCount : gtype.NewInt(),
closeQueue : gqueue.New(),
hooksMap : gmap.NewStringInterfaceMap(),
routers : gcache.New(),
cookies : gmap.NewIntInterfaceMap(),
sessions : gcache.New(),
@ -136,9 +142,16 @@ func (s *Server) handlerKey(domain, method, pattern string) string {
}
// 注册服务处理方法
func (s *Server) setHandler(domain, method, pattern string, item HandlerItem) {
s.hmu.Lock()
defer s.hmu.Unlock()
func (s *Server) setHandler(pattern string, item *HandlerItem) error {
domain, method, uri, err := s.parsePatternForBindHandler(pattern)
if err != nil {
return errors.New("invalid pattern")
}
item.uri = uri
item.domain = domain
item.method = method
// 静态注册
s.hmmu.Lock()
if method == gDEFAULT_METHOD {
for v, _ := range s.methodsMap {
s.handlerMap[s.handlerKey(domain, v, pattern)] = item
@ -146,7 +159,36 @@ func (s *Server) setHandler(domain, method, pattern string, item HandlerItem) {
} else {
s.handlerMap[s.handlerKey(domain, method, pattern)] = item
}
s.hmmu.Unlock()
// 动态注册,首先需要判断是否是动态注册,如果不是那么就没必要添加到动态注册记录变量中
if s.isUriHasRule(uri) {
array := strings.Split(uri, "/")
item.priority = len(array)
pattern := ""
for _, v := range array {
switch v[0] {
case ':':
case '*':
default:
if p == nil {
p = make(map[string]interface{})
p = p.(map[string]interface{})
}
if _, ok := p[v]; !ok {
p[v] = make(map[string]interface{})
}
}
}
}
return nil
}
// 判断URI中是否包含动态注册规则
func (s *Server) isUriHasRule(uri string) bool {
if len(uri) > 1 && (strings.Index(uri, "/:") != -1 || strings.Index(uri, "/*") != -1) {
return true
}
return false
}
// 解析pattern
@ -171,16 +213,11 @@ func (s *Server)parsePatternForBindHandler(pattern string) (domain, method, uri
// 绑定URI到操作函数/方法
// pattern的格式形如/user/list, put:/user, delete:/user, post:/user@johng.cn
// 支持RESTful的请求格式具体业务逻辑由绑定的处理方法来执行
func (s *Server)bindHandlerItem(pattern string, item HandlerItem) error {
func (s *Server)bindHandlerItem(pattern string, item *HandlerItem) error {
if s.status == 1 {
return errors.New("server handlers cannot be changed while running")
}
domain, method, uri, err := s.parsePatternForBindHandler(pattern)
if err != nil {
return errors.New("invalid pattern")
}
s.setHandler(domain, method, uri, item)
return nil
return s.setHandler(pattern, item)
}
// 通过映射数组绑定URI到操作函数/方法
@ -216,7 +253,11 @@ func (s *Server)appendMethodNameToUriWithPattern(pattern string, name string) st
// 注意该方法是直接绑定函数的内存地址,执行的时候直接执行该方法,不会存在初始化新的控制器逻辑
func (s *Server)BindHandler(pattern string, handler HandlerFunc) error {
return s.bindHandlerItem(pattern, HandlerItem{nil, "", handler})
return s.bindHandlerItem(pattern, &HandlerItem{
ctype : nil,
fname : "",
faddr : handler,
})
}
// 绑定对象到URI请求处理中会自动识别方法名称并附加到对应的URI地址后面
@ -228,7 +269,11 @@ func (s *Server)BindObject(pattern string, obj interface{}) error {
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = HandlerItem{nil, "", v.Method(i).Interface().(func(*Request))}
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
@ -244,7 +289,11 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, methods string
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = HandlerItem{nil, "", fval.Interface().(func(*Request))}
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : fval.Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
@ -261,7 +310,11 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
continue
}
key := name + ":" + pattern
m[key] = HandlerItem{nil, "", v.Method(i).Interface().(func(*Request))}
m[key] = &HandlerItem{
ctype : nil,
fname : "",
faddr : v.Method(i).Interface().(func(*Request)),
}
}
return s.bindHandlerByMap(m)
}
@ -279,7 +332,11 @@ func (s *Server)BindController(pattern string, c Controller) error {
continue
}
key := s.appendMethodNameToUriWithPattern(pattern, name)
m[key] = HandlerItem{v.Elem().Type(), name, nil}
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
@ -297,16 +354,21 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error {
for _, v := range strings.Split(gHTTP_METHODS, ",") {
methods[v] = true
}
// 如果存在与HttpMethod对应名字的方法那么绑定这些方法
for i := 0; i < v.NumMethod(); i++ {
name := t.Method(i).Name
name := strings.ToUpper(t.Method(i).Name)
if name == "Init" || name == "Shut" {
continue
}
if _, ok := s.methodsMap[strings.ToUpper(name)]; !ok {
if _, ok := s.methodsMap[name]; !ok {
continue
}
key := name + ":" + pattern
m[key] = HandlerItem{v.Elem().Type(), name, nil}
m[key] = &HandlerItem{
ctype : v.Elem().Type(),
fname : name,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
@ -323,31 +385,35 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, methods strin
return errors.New("invalid method name:" + method)
}
key := s.appendMethodNameToUriWithPattern(pattern, method)
m[key] = HandlerItem{ctype, method, nil}
m[key] = &HandlerItem{
ctype : ctype,
fname : method,
faddr : nil,
}
}
return s.bindHandlerByMap(m)
}
// 绑定指定的hook回调函数, pattern参数同BindHandler支持命名路由hook参数的值由ghttp server设定参数不区分大小写
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) error {
domain, method, uri, err := s.parsePatternForBindHandler(pattern)
if err != nil {
return errors.New("invalid pattern")
}
var l *glist.List
if method == gDEFAULT_METHOD {
for v, _ := range s.methodsMap {
if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil {
l = v.(*glist.List)
}
l.PushBack(handler)
}
} else {
if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil {
l = v.(*glist.List)
}
l.PushBack(handler)
}
//domain, method, uri, err := s.parsePatternForBindHookHandler(pattern)
//if err != nil {
// return errors.New("invalid pattern")
//}
//var l *glist.List
//if method == gDEFAULT_METHOD {
// for v, _ := range s.methodsMap {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, v, uri, hook), glist.New()); v != nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
// }
//} else {
// if v := s.hooksMap.GetWithDefault(s.handlerHookKey(domain, method, uri, hook), glist.New()); v == nil {
// l = v.(*glist.List)
// }
// l.PushBack(handler)
//}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/encoding/gjson"
"gitee.com/johng/gf/g/os/gtime"
)
func getByPattern() {
@ -95,6 +96,67 @@ func testConvert() {
}
}
func testSplitChar() {
var v interface{}
j := gjson.New(nil)
t1 := gtime.Nanosecond()
j.Set("a.b.c", 1)
t2 := gtime.Nanosecond()
fmt.Println(t2 - t1)
t5 := gtime.Nanosecond()
v = j.Get("a.b.c.d.e.f.g.h.i.j.k")
t6 := gtime.Nanosecond()
b, _ := j.ToJsonIndent()
fmt.Println(string(b))
fmt.Println(v)
fmt.Println(t6 - t5)
j.SetSplitChar('#')
t7 := gtime.Nanosecond()
v = j.Get("a#a#a#a#a#a#a#a#a#a#a#a#a#a#a#a")
t8 := gtime.Nanosecond()
fmt.Println(v)
fmt.Println(t8 - t7)
}
func testViolenceCheck() {
j := gjson.New(nil)
t1 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t2 := gtime.Nanosecond()
fmt.Println(t2 - t1)
t3 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t4 := gtime.Nanosecond()
fmt.Println(t4 - t3)
t5 := gtime.Nanosecond()
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
t6 := gtime.Nanosecond()
fmt.Println(t6 - t5)
j.SetViolenceCheck(false)
t7 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t8 := gtime.Nanosecond()
fmt.Println(t8 - t7)
t9 := gtime.Nanosecond()
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
t10 := gtime.Nanosecond()
fmt.Println(t10 - t9)
}
func main() {
testSet()
testSplitChar()
}

View File

@ -1,16 +1,39 @@
package main
import (
"gitee.com/johng/gf/g/encoding/gjson"
"fmt"
"gitee.com/johng/gf/g/util/gregx"
"gitee.com/johng/gf/g/os/gtime"
)
func main() {
t1 := gtime.Microsecond()
for i := 0; i < 10000; i++ {
gregx.MatchString(`([a-zA-Z]+)\^([a-zA-Z]+):(.+)@([\w\.\-]+)`, "a^b:c@d")
}
t2 := gtime.Microsecond()
j := gjson.New(nil)
t1 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t2 := gtime.Nanosecond()
fmt.Println(t2 - t1)
t3 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t4 := gtime.Nanosecond()
fmt.Println(t4 - t3)
t5 := gtime.Nanosecond()
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
t6 := gtime.Nanosecond()
fmt.Println(t6 - t5)
j.SetViolenceCheck(false)
t7 := gtime.Nanosecond()
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
t8 := gtime.Nanosecond()
fmt.Println(t8 - t7)
t9 := gtime.Nanosecond()
j.Get("a.a")
t10 := gtime.Nanosecond()
fmt.Println(t10 - t9)
}