mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
add consurrent safety parameter for gjson.Load/LoadContent; fulfil data type auto-check logics for gjson.LoadConetnt; update comment of gjson/gparser
This commit is contained in:
@ -4,9 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gjson provides flexible and useful API for JSON/XML/YAML/TOML content handling.
|
||||
//
|
||||
// JSON/XML/YAML/TOML数据格式处理。
|
||||
// Package gjson provides convenient API for JSON/XML/YAML/TOML data handling.
|
||||
package gjson
|
||||
|
||||
import (
|
||||
@ -19,7 +17,6 @@ import (
|
||||
"github.com/gogf/gf/g/encoding/gyaml"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/os/gfcache"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
@ -30,18 +27,24 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_SPLIT_CHAR = '.' // 默认层级分隔符号
|
||||
// Separator char for hierarchical data access.
|
||||
gDEFAULT_SPLIT_CHAR = '.'
|
||||
)
|
||||
|
||||
// json解析结果存放数组
|
||||
// The customized JSON struct.
|
||||
type Json struct {
|
||||
mu *rwmutex.RWMutex
|
||||
p *interface{} // 注意这是一个指针
|
||||
c byte // 层级分隔符,默认为"."
|
||||
vc bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率)
|
||||
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
|
||||
c byte // Char separator('.' in default).
|
||||
vc bool // Violence Check(false in default), which is used to access data
|
||||
// when the hierarchical data key contains separator char.
|
||||
}
|
||||
|
||||
// 将变量转换为Json对象进行处理,该变量至少应当是一个map或者slice,否者转换没有意义
|
||||
// New creates a Json object with any variable type of <data>,
|
||||
// but <data> should be a map or slice for data access reason,
|
||||
// or it will make no sense.
|
||||
// The <unsafe> param specifies whether using this Json object
|
||||
// in un-concurrent-safe context, which is false in default.
|
||||
func New(data interface{}, unsafe...bool) *Json {
|
||||
j := (*Json)(nil)
|
||||
switch data.(type) {
|
||||
@ -75,7 +78,7 @@ func New(data interface{}, unsafe...bool) *Json {
|
||||
return j
|
||||
}
|
||||
|
||||
// 创建一个非并发安全的Json对象
|
||||
// NewUnsafe creates a un-concurrent-safe Json object.
|
||||
func NewUnsafe(data...interface{}) *Json {
|
||||
if len(data) > 0 {
|
||||
return New(data[0], true)
|
||||
@ -83,17 +86,17 @@ func NewUnsafe(data...interface{}) *Json {
|
||||
return New(nil, true)
|
||||
}
|
||||
|
||||
// 识别当前给定内容是否为JSON格式
|
||||
// Valid checks whether <data> is a valid JSON data type.
|
||||
func Valid(data interface{}) bool {
|
||||
return json.Valid(gconv.Bytes(data))
|
||||
}
|
||||
|
||||
// 编码go变量为json字符串,并返回json字符串指针
|
||||
// Encode encodes <value> to JSON data type of bytes.
|
||||
func Encode(value interface{}) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
// 解码字符串/[]byte为interface{}变量
|
||||
// Decode decodes <data>(string/[]byte) to golang variable.
|
||||
func Decode(data interface{}) (interface{}, error) {
|
||||
var value interface{}
|
||||
if err := DecodeTo(gconv.Bytes(data), &value); err != nil {
|
||||
@ -103,58 +106,63 @@ func Decode(data interface{}) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 解析json字符串/[]byte为go变量,注意第二个参数为指针(任意结构的变量)
|
||||
// Decode decodes <data>(string/[]byte) to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func DecodeTo(data interface{}, v interface{}) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data)))
|
||||
decoder.UseNumber()
|
||||
return decoder.Decode(v)
|
||||
}
|
||||
|
||||
// 解析json字符串为gjson.Json对象,并返回操作对象指针
|
||||
func DecodeToJson(data interface{}) (*Json, error) {
|
||||
// DecodeToJson codes <data>(string/[]byte) to a Json object.
|
||||
func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) {
|
||||
if v, err := Decode(gconv.Bytes(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return New(v), nil
|
||||
return New(v, unsafe...), nil
|
||||
}
|
||||
}
|
||||
|
||||
// 支持多种配置文件类型转换为json格式内容并解析为gjson.Json对象
|
||||
func Load(path string) (*Json, error) {
|
||||
return LoadContent(gfcache.GetBinContents(path), gfile.Ext(path))
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Json object from its content.
|
||||
func Load(path string, unsafe...bool) (*Json, error) {
|
||||
return LoadContent(gfcache.GetBinContents(path), unsafe...)
|
||||
}
|
||||
|
||||
// 支持的配置文件格式:xml, json, yaml/yml, toml,
|
||||
// 默认为自动识别,当无法检测成功时使用json解析。
|
||||
func LoadContent(data interface{}, dataType...string) (*Json, error) {
|
||||
// LoadContent creates a Json object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
|
||||
var err error
|
||||
var result interface{}
|
||||
b := gconv.Bytes(data)
|
||||
t := "json"
|
||||
if len(dataType) > 0 {
|
||||
t = dataType[0]
|
||||
// auto check data type
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
} else if gregex.IsMatch(`^<.+>.*</.+>$`, b) {
|
||||
t = "xml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
|
||||
t = "yml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
|
||||
t = "toml"
|
||||
} else {
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
} else if gregex.IsMatch(`<.+>.*</.+>`, b) {
|
||||
t = "xml"
|
||||
} else if gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
|
||||
t = "yml"
|
||||
} else if gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
|
||||
t = "toml"
|
||||
}
|
||||
return nil, errors.New("unsupported data type")
|
||||
}
|
||||
// 其他数据格式解析
|
||||
// convert to json type data
|
||||
switch t {
|
||||
case "json", ".json":
|
||||
// ok
|
||||
case "xml", ".xml":
|
||||
// TODO UseNumber
|
||||
b, err = gxml.ToJson(b)
|
||||
|
||||
case "yml", "yaml", ".yml", ".yaml":
|
||||
// TODO UseNumber
|
||||
b, err = gyaml.ToJson(b)
|
||||
|
||||
case "toml", ".toml":
|
||||
// TODO UseNumber
|
||||
b, err = gtoml.ToJson(b)
|
||||
|
||||
default:
|
||||
@ -174,26 +182,26 @@ func LoadContent(data interface{}, dataType...string) (*Json, error) {
|
||||
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
|
||||
}
|
||||
}
|
||||
return New(result), nil
|
||||
return New(result, unsafe...), nil
|
||||
}
|
||||
|
||||
// 设置自定义的层级分隔符号
|
||||
// SetSplitChar sets the separator char for hierarchical data access.
|
||||
func (j *Json) SetSplitChar(char byte) {
|
||||
j.mu.Lock()
|
||||
j.c = char
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
|
||||
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
|
||||
func (j *Json) SetViolenceCheck(check bool) {
|
||||
// SetViolenceCheck enables/disables violence check for hierarchical data access.
|
||||
func (j *Json) SetViolenceCheck(enabled bool) {
|
||||
j.mu.Lock()
|
||||
j.vc = check
|
||||
j.vc = enabled
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// 将指定的json内容转换为指定结构返回,查找失败或者转换失败,目标对象转换为nil
|
||||
// 注意第二个参数需要给的是**变量地址**
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func (j *Json) GetToVar(pattern string, v interface{}) error {
|
||||
r := j.Get(pattern)
|
||||
if r != nil {
|
||||
@ -208,19 +216,18 @@ func (j *Json) GetToVar(pattern string, v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
|
||||
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
||||
// GetMap gets the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
func (j *Json) GetMap(pattern string) map[string]interface{} {
|
||||
result := j.Get(pattern)
|
||||
if result != nil {
|
||||
if r, ok := result.(map[string]interface{}); ok {
|
||||
return r
|
||||
}
|
||||
return gconv.Map(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将检索值转换为Json对象指针返回。
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// and converts it to a Json object.
|
||||
func (j *Json) GetJson(pattern string) *Json {
|
||||
result := j.Get(pattern)
|
||||
if result != nil {
|
||||
@ -229,7 +236,8 @@ func (j *Json) GetJson(pattern string) *Json {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将检索值转换为Json对象指针数组返回。
|
||||
// GetJsons gets the value by specified <pattern>,
|
||||
// and converts it to a slice of Json object.
|
||||
func (j *Json) GetJsons(pattern string) []*Json {
|
||||
array := j.GetArray(pattern)
|
||||
if len(array) > 0 {
|
||||
@ -242,21 +250,25 @@ func (j *Json) GetJsons(pattern string) []*Json {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换。
|
||||
// GetArray gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (j *Json) GetArray(pattern string) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern))
|
||||
}
|
||||
|
||||
// 返回指定json中的string
|
||||
// GetString gets the value by specified <pattern>,
|
||||
// and converts it to string.
|
||||
func (j *Json) GetString(pattern string) string {
|
||||
return gconv.String(j.Get(pattern))
|
||||
}
|
||||
|
||||
// 返回指定json中的strings(转换为[]string数组)
|
||||
// GetStrings gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []string.
|
||||
func (j *Json) GetStrings(pattern string) []string {
|
||||
return gconv.Strings(j.Get(pattern))
|
||||
}
|
||||
|
||||
// See GetArray.
|
||||
func (j *Json) GetInterfaces(pattern string) []interface{} {
|
||||
return gconv.Interfaces(j.Get(pattern))
|
||||
}
|
||||
@ -269,7 +281,10 @@ func (j *Json) GetTimeDuration(pattern string) time.Duration {
|
||||
return gconv.TimeDuration(j.Get(pattern))
|
||||
}
|
||||
|
||||
// 返回指定json中的bool(false:"", 0, false, off)
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
func (j *Json) GetBool(pattern string) bool {
|
||||
return gconv.Bool(j.Get(pattern))
|
||||
}
|
||||
@ -330,25 +345,29 @@ func (j *Json) GetFloats(pattern string) []float64 {
|
||||
return gconv.Floats(j.Get(pattern))
|
||||
}
|
||||
|
||||
// 将指定变量转换为struct对象(对象属性赋值)
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (j *Json) GetToStruct(pattern string, objPointer interface{}) error {
|
||||
return gconv.Struct(j.Get(pattern), objPointer)
|
||||
}
|
||||
|
||||
// 动态设置层级变量
|
||||
// Set sets value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Set(pattern string, value interface{}) error {
|
||||
return j.setValue(pattern, value, false)
|
||||
}
|
||||
|
||||
// 动态删除层级变量
|
||||
// Remove deletes value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (j *Json) Remove(pattern string) error {
|
||||
return j.setValue(pattern, nil, true)
|
||||
}
|
||||
|
||||
// 根据pattern查找并设置数据
|
||||
// 注意:
|
||||
// 1、写入的value为nil且removed为true时,表示删除;
|
||||
// 2、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值;
|
||||
// Set <value> by <pattern>.
|
||||
// Notice:
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
|
||||
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
array := strings.Split(pattern, string(j.c))
|
||||
length := len(array)
|
||||
@ -367,144 +386,144 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
defer j.mu.Unlock()
|
||||
for i:= 0; i < length; i++ {
|
||||
switch (*pointer).(type) {
|
||||
case map[string]interface{}:
|
||||
if i == length - 1 {
|
||||
if removed && value == nil {
|
||||
// 删除map元素
|
||||
delete((*pointer).(map[string]interface{}), array[i])
|
||||
} else {
|
||||
(*pointer).(map[string]interface{})[array[i]] = value
|
||||
}
|
||||
} else {
|
||||
// 当键名不存在的情况这里会进行处理
|
||||
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
// 创建新节点
|
||||
if gstr.IsNumeric(array[i + 1]) {
|
||||
// 创建array节点
|
||||
n, _ := strconv.Atoi(array[i + 1])
|
||||
var v interface{} = make([]interface{}, n + 1)
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
} else {
|
||||
// 创建map节点
|
||||
var v interface{} = make(map[string]interface{})
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
}
|
||||
} else {
|
||||
pparent = pointer
|
||||
pointer = &v
|
||||
}
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
// 键名与当前指针类型不符合,需要执行**覆盖操作**
|
||||
if !gstr.IsNumeric(array[i]) {
|
||||
case map[string]interface{}:
|
||||
if i == length - 1 {
|
||||
*pointer = map[string]interface{}{ array[i] : value }
|
||||
if removed && value == nil {
|
||||
// 删除map元素
|
||||
delete((*pointer).(map[string]interface{}), array[i])
|
||||
} else {
|
||||
(*pointer).(map[string]interface{})[array[i]] = value
|
||||
}
|
||||
} else {
|
||||
var v interface{} = make(map[string]interface{})
|
||||
*pointer = v
|
||||
pparent = pointer
|
||||
pointer = &v
|
||||
// 当键名不存在的情况这里会进行处理
|
||||
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
// 创建新节点
|
||||
if gstr.IsNumeric(array[i + 1]) {
|
||||
// 创建array节点
|
||||
n, _ := strconv.Atoi(array[i + 1])
|
||||
var v interface{} = make([]interface{}, n + 1)
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
} else {
|
||||
// 创建map节点
|
||||
var v interface{} = make(map[string]interface{})
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
}
|
||||
} else {
|
||||
pparent = pointer
|
||||
pointer = &v
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
valn, err := strconv.Atoi(array[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 叶子节点
|
||||
if i == length - 1 {
|
||||
if len((*pointer).([]interface{})) > valn {
|
||||
if removed && value == nil {
|
||||
// 删除数据元素
|
||||
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
|
||||
case []interface{}:
|
||||
// 键名与当前指针类型不符合,需要执行**覆盖操作**
|
||||
if !gstr.IsNumeric(array[i]) {
|
||||
if i == length - 1 {
|
||||
*pointer = map[string]interface{}{ array[i] : value }
|
||||
} else {
|
||||
(*pointer).([]interface{})[valn] = value
|
||||
}
|
||||
} else {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
if pparent == nil {
|
||||
// 表示根节点
|
||||
j.setPointerWithValue(pointer, array[i], value)
|
||||
} else {
|
||||
// 非根节点
|
||||
s := make([]interface{}, valn + 1)
|
||||
copy(s, (*pointer).([]interface{}))
|
||||
s[valn] = value
|
||||
j.setPointerWithValue(pparent, array[i - 1], s)
|
||||
var v interface{} = make(map[string]interface{})
|
||||
*pointer = v
|
||||
pparent = pointer
|
||||
pointer = &v
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if gstr.IsNumeric(array[i + 1]) {
|
||||
n, _ := strconv.Atoi(array[i + 1])
|
||||
|
||||
valn, err := strconv.Atoi(array[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 叶子节点
|
||||
if i == length - 1 {
|
||||
if len((*pointer).([]interface{})) > valn {
|
||||
(*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
|
||||
pparent = pointer
|
||||
pointer = &(*pointer).([]interface{})[valn]
|
||||
if removed && value == nil {
|
||||
// 删除数据元素
|
||||
j.setPointerWithValue(pparent, array[i - 1], append((*pointer).([]interface{})[ : valn], (*pointer).([]interface{})[valn + 1 : ]...))
|
||||
} else {
|
||||
(*pointer).([]interface{})[valn] = value
|
||||
}
|
||||
} else {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
var v interface{} = make([]interface{}, n + 1)
|
||||
if pparent == nil {
|
||||
// 表示根节点
|
||||
j.setPointerWithValue(pointer, array[i], value)
|
||||
} else {
|
||||
// 非根节点
|
||||
s := make([]interface{}, valn + 1)
|
||||
copy(s, (*pointer).([]interface{}))
|
||||
s[valn] = value
|
||||
j.setPointerWithValue(pparent, array[i - 1], s)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if gstr.IsNumeric(array[i + 1]) {
|
||||
n, _ := strconv.Atoi(array[i + 1])
|
||||
if len((*pointer).([]interface{})) > valn {
|
||||
(*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
|
||||
pparent = pointer
|
||||
pointer = &(*pointer).([]interface{})[valn]
|
||||
} else {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
var v interface{} = make([]interface{}, n + 1)
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
}
|
||||
} else {
|
||||
var v interface{} = make(map[string]interface{})
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
}
|
||||
} else {
|
||||
var v interface{} = make(map[string]interface{})
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前指针指向的变量不是引用类型的,
|
||||
// 那么修改变量必须通过父级进行修改,即 pparent
|
||||
default:
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
if gstr.IsNumeric(array[i]) {
|
||||
n, _ := strconv.Atoi(array[i])
|
||||
s := make([]interface{}, n + 1)
|
||||
if i == length - 1 {
|
||||
s[n] = value
|
||||
default:
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
if pparent != nil {
|
||||
pparent = j.setPointerWithValue(pparent, array[i - 1], s)
|
||||
} else {
|
||||
*pointer = s
|
||||
pparent = pointer
|
||||
}
|
||||
} else {
|
||||
var v interface{} = make(map[string]interface{})
|
||||
if i == length - 1 {
|
||||
v = map[string]interface{}{
|
||||
array[i] : value,
|
||||
if gstr.IsNumeric(array[i]) {
|
||||
n, _ := strconv.Atoi(array[i])
|
||||
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
|
||||
}
|
||||
}
|
||||
if pparent != nil {
|
||||
pparent = j.setPointerWithValue(pparent, array[i - 1], v)
|
||||
} else {
|
||||
*pointer = v
|
||||
pparent = pointer
|
||||
var v interface{} = make(map[string]interface{})
|
||||
if i == length - 1 {
|
||||
v = map[string]interface{}{
|
||||
array[i] : value,
|
||||
}
|
||||
}
|
||||
if pparent != nil {
|
||||
pparent = j.setPointerWithValue(pparent, array[i - 1], v)
|
||||
} else {
|
||||
*pointer = v
|
||||
pparent = pointer
|
||||
}
|
||||
pointer = &v
|
||||
}
|
||||
pointer = &v
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
done:
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据结构转换,map参数必须转换为map[string]interface{},数组参数必须转换为[]interface{}
|
||||
// Convert <value> to map[string]interface{} or []interface{},
|
||||
// which can be supported for hierarchical data access.
|
||||
func (j *Json) convertValue(value interface{}) interface{} {
|
||||
switch value.(type) {
|
||||
case map[string]interface{}:
|
||||
@ -524,7 +543,7 @@ func (j *Json) convertValue(value interface{}) interface{} {
|
||||
case reflect.Map: return gconv.Map(value)
|
||||
case reflect.Struct: return gconv.Map(value)
|
||||
default:
|
||||
// 最后使用JSON编解码
|
||||
// Use json decode/encode at last.
|
||||
b, _ := Encode(value)
|
||||
v, _ := Decode(b)
|
||||
return v
|
||||
@ -532,8 +551,8 @@ func (j *Json) convertValue(value interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// 用于Set方法中,对指针指向的内存地址进行赋值
|
||||
// 返回修改后的父级指针
|
||||
// Set <key>:<value> to <pointer>, the <key> may be a map key or slice index.
|
||||
// It returns the pointer to the new value set.
|
||||
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
|
||||
switch (*pointer).(type) {
|
||||
case map[string]interface{}:
|
||||
@ -557,9 +576,12 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
|
||||
return pointer
|
||||
}
|
||||
|
||||
// 根据约定字符串方式访问json解析数据,参数形如: "items.name.first", "list.0"; 当pattern为空时,表示获取所有数据;
|
||||
// 返回的结果类型的interface{},因此需要自己做类型转换;
|
||||
// 如果找不到对应节点的数据,返回nil;
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
func (j *Json) Get(pattern...string) interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
@ -580,13 +602,14 @@ func (j *Json) Get(pattern...string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断锁给定pattern是否数据存在
|
||||
// Contains checks whether the value by specified <pattern> exist.
|
||||
func (j *Json) Contains(pattern...string) bool {
|
||||
return j.Get(pattern...) != nil
|
||||
}
|
||||
|
||||
// 计算指定pattern的元素长度(pattern对应数据类型为map/slice时有效)。
|
||||
// 当pattern对应的数据类型非map/slice时,返回-1。
|
||||
// Len returns the length/size of the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice or map.
|
||||
// It returns -1 if the target value is not found, or its type is invalid.
|
||||
func (j *Json) Len(pattern string) int {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p != nil {
|
||||
@ -602,7 +625,8 @@ func (j *Json) Len(pattern string) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// 指定pattern追加元素
|
||||
// Append appends value to the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice.
|
||||
func (j *Json) Append(pattern string, value interface{}) error {
|
||||
p := j.getPointerByPattern(pattern)
|
||||
if p == nil {
|
||||
@ -615,7 +639,7 @@ func (j *Json) Append(pattern string, value interface{}) error {
|
||||
return fmt.Errorf("invalid variable type of %s", pattern)
|
||||
}
|
||||
|
||||
// 根据pattern获取对应元素项的指针
|
||||
// Get a pointer to the value by specified <pattern>.
|
||||
func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
||||
if j.vc {
|
||||
return j.getPointerByPatternWithViolenceCheck(pattern)
|
||||
@ -624,18 +648,7 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// 根据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;
|
||||
// 这样检索的复杂度很高,主要是为了避免键名中存在分隔符号(默认为".")的情况,避免歧义。
|
||||
// Get a pointer to the value of specified <pattern> with violence check.
|
||||
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
|
||||
if !j.vc {
|
||||
return j.getPointerByPatternWithoutViolenceCheck(pattern)
|
||||
@ -661,7 +674,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
|
||||
pointer = r
|
||||
}
|
||||
} else {
|
||||
// 查找下一个分割符号的索引位置
|
||||
// Get the position for next separator char.
|
||||
index = strings.LastIndexByte(pattern[start:index], j.c)
|
||||
if index != -1 && length > 0 {
|
||||
index += length + 1
|
||||
@ -674,7 +687,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 层级检索,内部不执行分隔符冲突检查,检索效率会有所提高,但是冲突需要开发者自己根据自定义的分隔符来进行解决
|
||||
// Get a pointer to the value of specified <pattern>, with no violence check.
|
||||
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
|
||||
if j.vc {
|
||||
return j.getPointerByPatternWithViolenceCheck(pattern)
|
||||
@ -698,8 +711,8 @@ func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interfac
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断给定的key在当前的pointer下是否有值,并返回对应的pointer,
|
||||
// 注意这里返回的指针都是临时变量的内存地址
|
||||
// Check whether there's value by <key> in specified <pointer>.
|
||||
// It returns a pointer to the value.
|
||||
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
|
||||
switch (*pointer).(type) {
|
||||
case map[string]interface{}:
|
||||
@ -717,7 +730,8 @@ func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interfac
|
||||
return nil
|
||||
}
|
||||
|
||||
// 转换为map[string]interface{}类型,如果转换失败,返回nil
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
@ -729,15 +743,16 @@ func (j *Json) ToMap() map[string]interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为[]interface{}类型,如果转换失败,返回nil
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
switch (*(j.p)).(type) {
|
||||
case []interface{}:
|
||||
return (*(j.p)).([]interface{})
|
||||
default:
|
||||
return nil
|
||||
case []interface{}:
|
||||
return (*(j.p)).([]interface{})
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -803,14 +818,15 @@ func (j *Json) ToTomlString() (string, error) {
|
||||
return string(b), e
|
||||
}
|
||||
|
||||
// 转换为指定的struct对象
|
||||
func (j *Json) ToStruct(o interface{}) error {
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
func (j *Json) ToStruct(objPointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), o)
|
||||
return gconv.Struct(*(j.p), objPointer)
|
||||
}
|
||||
|
||||
// 打印Json对象
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
|
||||
@ -69,7 +69,7 @@ func Test_Load_XML(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_YAML(t *testing.T) {
|
||||
func Test_Load_YAML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a:
|
||||
- 1
|
||||
@ -104,7 +104,16 @@ m:
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML(t *testing.T) {
|
||||
func Test_Load_YAML2(t *testing.T) {
|
||||
data := []byte("i : 123456789")
|
||||
gtest.Case(t, func() {
|
||||
j, err := gjson.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("i"), "123456789")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a = ["1", "2", "3"]
|
||||
n = "123456789"
|
||||
@ -136,3 +145,12 @@ n = "123456789"
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML2(t *testing.T) {
|
||||
data := []byte("i=123456789")
|
||||
gtest.Case(t, func() {
|
||||
j, err := gjson.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("i"), "123456789")
|
||||
})
|
||||
}
|
||||
|
||||
@ -172,7 +172,7 @@ func Test_Set10(t *testing.T) {
|
||||
|
||||
func Test_Set11(t *testing.T) {
|
||||
e := []byte(`{"a":{"b":{}}}`)
|
||||
p, _ := gjson.LoadContent([]byte(`{"a":{"b":{"c":1}}}`), "json")
|
||||
p, _ := gjson.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
|
||||
p.Remove("a.b.c")
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gp.
|
||||
|
||||
// Package gparser provides a flexible and easy way for accessing/converting variable and JSON/XML/YAML/TOML contents.
|
||||
// Package gparser provides convenient API for accessing/converting variable and JSON/XML/YAML/TOML.
|
||||
package gparser
|
||||
|
||||
import (
|
||||
@ -16,13 +16,16 @@ type Parser struct {
|
||||
json *gjson.Json
|
||||
}
|
||||
|
||||
// 将变量转换为Parser对象进行处理,该变量至少应当是一个map或者array,否者转换没有意义
|
||||
// value可以传递nil, 表示创建一个空的Parser对象
|
||||
func New (value interface{}, unsafe...bool) *Parser {
|
||||
// New creates a Parser object with any variable type of <data>,
|
||||
// but <data> should be a map or slice for data access reason,
|
||||
// or it will make no sense.
|
||||
// The <unsafe> param specifies whether using this Parser object
|
||||
// in un-concurrent-safe context, which is false in default.
|
||||
func New(value interface{}, unsafe...bool) *Parser {
|
||||
return &Parser{gjson.New(value, unsafe...)}
|
||||
}
|
||||
|
||||
// 非并发安全Parser对象
|
||||
// NewUnsafe creates a un-concurrent-safe Parser object.
|
||||
func NewUnsafe (value...interface{}) *Parser {
|
||||
if len(value) > 0 {
|
||||
return &Parser{gjson.New(value[0], false)}
|
||||
@ -30,57 +33,64 @@ func NewUnsafe (value...interface{}) *Parser {
|
||||
return &Parser{gjson.New(nil, false)}
|
||||
}
|
||||
|
||||
func Load (path string) (*Parser, error) {
|
||||
if j, e := gjson.Load(path); e == nil {
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Parser object from its content.
|
||||
func Load (path string, unsafe...bool) (*Parser, error) {
|
||||
if j, e := gjson.Load(path, unsafe...); e == nil {
|
||||
return &Parser{j}, nil
|
||||
} else {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
// 支持的数据内容格式:json(默认), xml, yaml/yml, toml
|
||||
func LoadContent (data []byte, dataType...string) (*Parser, error) {
|
||||
if j, e := gjson.LoadContent(data, dataType...); e == nil {
|
||||
// LoadContent creates a Parser object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent (data []byte, unsafe...bool) (*Parser, error) {
|
||||
if j, e := gjson.LoadContent(data, unsafe...); e == nil {
|
||||
return &Parser{j}, nil
|
||||
} else {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
// 设置自定义的层级分隔符号
|
||||
// SetSplitChar sets the separator char for hierarchical data access.
|
||||
func (p *Parser) SetSplitChar(char byte) {
|
||||
p.json.SetSplitChar(char)
|
||||
}
|
||||
|
||||
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
|
||||
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
|
||||
// SetViolenceCheck enables/disables violence check for hierarchical data access.
|
||||
func (p *Parser) SetViolenceCheck(check bool) {
|
||||
p.json.SetViolenceCheck(check)
|
||||
}
|
||||
|
||||
// 将指定的json内容转换为指定结构返回,查找失败或者转换失败,目标对象转换为nil
|
||||
// 注意第二个参数需要给的是变量地址
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
func (p *Parser) GetToVar(pattern string, v interface{}) error {
|
||||
return p.json.GetToVar(pattern, v)
|
||||
}
|
||||
|
||||
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
|
||||
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
||||
// GetMap gets the value by specified <pattern>,
|
||||
// and converts it to map[string]interface{}.
|
||||
func (p *Parser) GetMap(pattern string) map[string]interface{} {
|
||||
return p.json.GetMap(pattern)
|
||||
}
|
||||
|
||||
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
|
||||
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
||||
// GetArray gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []interface{}.
|
||||
func (p *Parser) GetArray(pattern string) []interface{} {
|
||||
return p.json.GetArray(pattern)
|
||||
}
|
||||
|
||||
// 返回指定json中的string
|
||||
// GetString gets the value by specified <pattern>,
|
||||
// and converts it to string.
|
||||
func (p *Parser) GetString(pattern string) string {
|
||||
return p.json.GetString(pattern)
|
||||
}
|
||||
|
||||
// GetStrings gets the value by specified <pattern>,
|
||||
// and converts it to a slice of []string.
|
||||
func (p *Parser) GetStrings(pattern string) []string {
|
||||
return p.json.GetStrings(pattern)
|
||||
}
|
||||
@ -97,7 +107,10 @@ func (p *Parser) GetTimeDuration(pattern string) time.Duration {
|
||||
return p.json.GetTimeDuration(pattern)
|
||||
}
|
||||
|
||||
// 返回指定json中的bool(false:"", 0, false, off)
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
// or returns true instead.
|
||||
func (p *Parser) GetBool(pattern string) bool {
|
||||
return p.json.GetBool(pattern)
|
||||
}
|
||||
@ -158,51 +171,60 @@ func (p *Parser) GetFloats(pattern string) []float64 {
|
||||
return p.json.GetFloats(pattern)
|
||||
}
|
||||
|
||||
// 将指定变量转换为struct对象(对象属性赋值)
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error {
|
||||
return p.json.GetToStruct(pattern, objPointer)
|
||||
}
|
||||
|
||||
// 根据pattern查找并设置数据
|
||||
// 注意:写入的时候"."符号只能表示层级,不能使用带"."符号的键名
|
||||
// Set sets value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (p *Parser) Set(pattern string, value interface{}) error {
|
||||
return p.json.Set(pattern, value)
|
||||
}
|
||||
|
||||
// 计算指定pattern的元素长度(pattern对应数据类型为map[string]interface{}/[]interface{}时有效)
|
||||
// Len returns the length/size of the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice or map.
|
||||
// It returns -1 if the target value is not found, or its type is invalid.
|
||||
func (p *Parser) Len(pattern string) int {
|
||||
return p.json.Len(pattern)
|
||||
}
|
||||
|
||||
// 指定pattern追加元素
|
||||
// Append appends value to the value by specified <pattern>.
|
||||
// The target value by <pattern> should be type of slice.
|
||||
func (p *Parser) Append(pattern string, value interface{}) error {
|
||||
return p.json.Append(pattern, value)
|
||||
}
|
||||
|
||||
// 动态删除变量节点
|
||||
// Remove deletes value with specified <pattern>.
|
||||
// It supports hierarchical data access by char separator, which is '.' in default.
|
||||
func (p *Parser) Remove(pattern string) error {
|
||||
return p.json.Remove(pattern)
|
||||
}
|
||||
|
||||
// 根据约定字符串方式访问json解析数据,参数形如: "items.name.first", "list.0"; 当pattern为空时,表示获取所有数据
|
||||
// 返回的结果类型的interface{},因此需要自己做类型转换;
|
||||
// 如果找不到对应节点的数据,返回nil;
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
//
|
||||
// We can also access slice item by its index number in <pattern>,
|
||||
// eg: "items.name.first", "list.10".
|
||||
func (p *Parser) Get(pattern...string) interface{} {
|
||||
return p.json.Get(pattern...)
|
||||
}
|
||||
|
||||
// 转换为map[string]interface{}类型,如果转换失败,返回nil
|
||||
// ToMap converts current object values to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (p *Parser) ToMap() map[string]interface{} {
|
||||
return p.json.ToMap()
|
||||
}
|
||||
|
||||
// 转换为[]interface{}类型,如果转换失败,返回nil
|
||||
// ToArray converts current object values to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (p *Parser) ToArray() []interface{} {
|
||||
return p.json.ToArray()
|
||||
}
|
||||
|
||||
/* 以下为数据文件格式转换,支持类型:xml, json, yaml/yml, toml */
|
||||
|
||||
func (p *Parser) ToXml(rootTag...string) ([]byte, error) {
|
||||
return p.json.ToXml(rootTag...)
|
||||
}
|
||||
@ -227,12 +249,13 @@ func (p *Parser) ToToml() ([]byte, error) {
|
||||
return p.json.ToToml()
|
||||
}
|
||||
|
||||
// 打印Json对象
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (p *Parser) Dump() error {
|
||||
return p.json.Dump()
|
||||
}
|
||||
|
||||
// 将变量解析为对应的struct对象,注意传递的参数为struct对象指针
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
func (p *Parser) ToStruct(o interface{}) error {
|
||||
return p.json.ToStruct(o)
|
||||
}
|
||||
@ -261,7 +284,6 @@ func VarToToml(value interface{}) ([]byte, error) {
|
||||
return New(value).ToToml()
|
||||
}
|
||||
|
||||
// 将变量解析为对应的struct对象,注意传递的参数为struct对象指针
|
||||
func VarToStruct(value interface{}, obj interface{}) error {
|
||||
return New(value).ToStruct(obj)
|
||||
}
|
||||
|
||||
208
g/encoding/gparser/gparser_unit_basic_test.go
Normal file
208
g/encoding/gparser/gparser_unit_basic_test.go
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_New(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NewUnsafe(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.NewUnsafe(data)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Encode(t *testing.T) {
|
||||
value := g.Slice{1, 2, 3}
|
||||
gtest.Case(t, func() {
|
||||
b, err := gparser.VarToJson(value)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(b, []byte(`[1,2,3]`))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Decode(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SplitChar(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
j.SetSplitChar(byte('#'))
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m#k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a#1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ViolenceCheck(t *testing.T) {
|
||||
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.Assert(j.Get("m.a.2"), 3)
|
||||
gtest.Assert(j.Get("m.v1.v2"), nil)
|
||||
j.SetViolenceCheck(true)
|
||||
gtest.Assert(j.Get("m.v1.v2"), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetToVar(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
var m map[string]string
|
||||
var n int
|
||||
var a []int
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
|
||||
j.GetToVar("n", &n)
|
||||
j.GetToVar("m", &m)
|
||||
j.GetToVar("a", &a)
|
||||
|
||||
gtest.Assert(n, "123456789")
|
||||
gtest.Assert(m, g.Map{"k" : "v"})
|
||||
gtest.Assert(a, g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetMap(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.Assert(j.GetMap("n"), g.Map{})
|
||||
gtest.Assert(j.GetMap("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.GetMap("a"), g.Map{})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetArray(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.Assert(j.GetArray("n"), g.Array{123456789})
|
||||
gtest.Assert(j.GetArray("m"), g.Array{g.Map{"k":"v"}})
|
||||
gtest.Assert(j.GetArray("a"), g.Array{1,2,3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetString(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.AssertEQ(j.GetString("n"), "123456789")
|
||||
gtest.AssertEQ(j.GetString("m"), `{"k":"v"}`)
|
||||
gtest.AssertEQ(j.GetString("a"), `[1,2,3]`)
|
||||
gtest.AssertEQ(j.GetString("i"), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetStrings(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.AssertEQ(j.GetStrings("n"), g.SliceStr{"123456789"})
|
||||
gtest.AssertEQ(j.GetStrings("m"), g.SliceStr{`{"k":"v"}`})
|
||||
gtest.AssertEQ(j.GetStrings("a"), g.SliceStr{"1", "2", "3"})
|
||||
gtest.AssertEQ(j.GetStrings("i"), g.SliceStr{})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_GetInterfaces(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
gtest.Case(t, func() {
|
||||
j := gparser.New(data)
|
||||
gtest.AssertNE(j, nil)
|
||||
gtest.AssertEQ(j.GetInterfaces("n"), g.Array{123456789})
|
||||
gtest.AssertEQ(j.GetInterfaces("m"), g.Array{g.Map{"k":"v"}})
|
||||
gtest.AssertEQ(j.GetInterfaces("a"), g.Array{1,2,3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Len(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Append("a", 1)
|
||||
p.Append("a", 2)
|
||||
gtest.Assert(p.Len("a"), 2)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Append("a.b", 1)
|
||||
p.Append("a.c", 2)
|
||||
gtest.Assert(p.Len("a"), 2)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Set("a", 1)
|
||||
gtest.Assert(p.Len("a"), -1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Append("a", 1)
|
||||
p.Append("a", 2)
|
||||
gtest.Assert(p.Get("a"), g.Slice{1, 2})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Append("a.b", 1)
|
||||
p.Append("a.c", 2)
|
||||
gtest.Assert(p.Get("a"), g.Map{
|
||||
"b" : g.Slice{1},
|
||||
"c" : g.Slice{2},
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
p := gparser.New(nil)
|
||||
p.Set("a", 1)
|
||||
err := p.Append("a", 2)
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(p.Get("a"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
156
g/encoding/gparser/gparser_unit_load_test.go
Normal file
156
g/encoding/gparser/gparser_unit_load_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func Test_Load_JSON(t *testing.T) {
|
||||
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
// JSON
|
||||
gtest.Case(t, func() {
|
||||
path := "test.json"
|
||||
gfile.PutBinContents(path, data)
|
||||
defer gfile.Remove(path)
|
||||
j, err := gparser.Load(path)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_XML(t *testing.T) {
|
||||
data := []byte(`<doc><a>1</a><a>2</a><a>3</a><m><k>v</k></m><n>123456789</n></doc>`)
|
||||
// XML
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
// XML
|
||||
gtest.Case(t, func() {
|
||||
path := "test.xml"
|
||||
gfile.PutBinContents(path, data)
|
||||
defer gfile.Remove(path)
|
||||
j, err := gparser.Load(path)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_YAML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
m:
|
||||
k: v
|
||||
"n": 123456789
|
||||
`)
|
||||
// YAML
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
// YAML
|
||||
gtest.Case(t, func() {
|
||||
path := "test.yaml"
|
||||
gfile.PutBinContents(path, data)
|
||||
defer gfile.Remove(path)
|
||||
j, err := gparser.Load(path)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_YAML2(t *testing.T) {
|
||||
data := []byte("i : 123456789")
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("i"), "123456789")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a = ["1", "2", "3"]
|
||||
n = "123456789"
|
||||
|
||||
[m]
|
||||
k = "v"
|
||||
`)
|
||||
// TOML
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
// TOML
|
||||
gtest.Case(t, func() {
|
||||
path := "test.toml"
|
||||
gfile.PutBinContents(path, data)
|
||||
defer gfile.Remove(path)
|
||||
j, err := gparser.Load(path)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k" : "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Load_TOML2(t *testing.T) {
|
||||
data := []byte("i=123456789")
|
||||
gtest.Case(t, func() {
|
||||
j, err := gparser.LoadContent(data)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(j.Get("i"), "123456789")
|
||||
})
|
||||
}
|
||||
@ -4,8 +4,6 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// 单元测试
|
||||
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
@ -22,7 +20,6 @@ func Test_Set1(t *testing.T) {
|
||||
})
|
||||
p.Set("k1.k11", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, []byte(`{"k1":{"k11":[1,2,3]},"k2":"v2"}`)) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -36,7 +33,6 @@ func Test_Set2(t *testing.T) {
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("0.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -48,11 +44,10 @@ func Test_Set2(t *testing.T) {
|
||||
func Test_Set3(t *testing.T) {
|
||||
e := []byte(`{"kv":{"k1":"v1"}}`)
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("kv", map[string]string{
|
||||
p.Set("kv", map[string]string {
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -68,7 +63,6 @@ func Test_Set4(t *testing.T) {
|
||||
"k1" : "v1",
|
||||
})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -82,7 +76,6 @@ func Test_Set5(t *testing.T) {
|
||||
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 {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -96,7 +89,6 @@ func Test_Set6(t *testing.T) {
|
||||
p := gparser.New([]string{"a"})
|
||||
p.Set("1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -113,7 +105,6 @@ func Test_Set7(t *testing.T) {
|
||||
})
|
||||
p.Set("0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -130,7 +121,6 @@ func Test_Set8(t *testing.T) {
|
||||
})
|
||||
p.Set("0.0.0.0.0.0.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -147,7 +137,6 @@ func Test_Set9(t *testing.T) {
|
||||
})
|
||||
p.Set("k1.1", []int{1,2,3})
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -162,7 +151,6 @@ func Test_Set10(t *testing.T) {
|
||||
p := gparser.New(nil)
|
||||
p.Set("a.b.c", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -174,10 +162,9 @@ func Test_Set10(t *testing.T) {
|
||||
|
||||
func Test_Set11(t *testing.T) {
|
||||
e := []byte(`{"a":{"b":{}}}`)
|
||||
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`), "json")
|
||||
p, _ := gparser.LoadContent([]byte(`{"a":{"b":{"c":1}}}`))
|
||||
p.Remove("a.b.c")
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -192,7 +179,6 @@ func Test_Set12(t *testing.T) {
|
||||
p.Set("0", 0)
|
||||
p.Set("1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -207,7 +193,6 @@ func Test_Set13(t *testing.T) {
|
||||
p.Set("array.0", 0)
|
||||
p.Set("array.1", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
@ -216,7 +201,16 @@ func Test_Set13(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
func Test_Set14(t *testing.T) {
|
||||
e := []byte(`{"f":{"a":1}}`)
|
||||
p := gparser.New(nil)
|
||||
p.Set("f", "m")
|
||||
p.Set("f.a", 1)
|
||||
if c, err := p.ToJson(); err == nil {
|
||||
if bytes.Compare(c, e) != 0 {
|
||||
t.Error("expect:", string(e))
|
||||
}
|
||||
} else {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ func getServerFdMap() map[string]listenerFdMap {
|
||||
func bufferToServerFdMap(buffer []byte) map[string]listenerFdMap {
|
||||
sfm := make(map[string]listenerFdMap)
|
||||
if len(buffer) > 0 {
|
||||
j, _ := gjson.LoadContent(buffer, "json")
|
||||
j, _ := gjson.LoadContent(buffer)
|
||||
for k, _ := range j.ToMap() {
|
||||
m := make(map[string]string)
|
||||
for k, v := range j.GetMap(k) {
|
||||
|
||||
Reference in New Issue
Block a user