2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
//
|
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
|
2019-01-15 23:27:47 +08:00
|
|
|
|
// Package gjson provides quite flexible and useful API for JSON/XML/YAML/TOML content handling.
|
2017-11-23 10:21:28 +08:00
|
|
|
|
package gjson
|
|
|
|
|
|
|
|
|
|
import (
|
2017-12-25 17:04:54 +08:00
|
|
|
|
"encoding/json"
|
2019-03-12 00:24:31 +08:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/gogf/gf/g/encoding/gtoml"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/encoding/gxml"
|
|
|
|
|
"github.com/gogf/gf/g/encoding/gyaml"
|
2019-03-12 00:24:31 +08:00
|
|
|
|
"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"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/text/gstr"
|
2019-03-12 00:24:31 +08:00
|
|
|
|
"github.com/gogf/gf/g/util/gconv"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
2018-07-01 00:27:33 +08:00
|
|
|
|
"time"
|
2017-11-23 10:21:28 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-04-12 14:09:33 +08:00
|
|
|
|
const (
|
|
|
|
|
gDEFAULT_SPLIT_CHAR = '.' // 默认层级分隔符号
|
|
|
|
|
)
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// json解析结果存放数组
|
|
|
|
|
type Json struct {
|
2018-09-13 18:40:04 +08:00
|
|
|
|
mu *rwmutex.RWMutex
|
2018-01-23 15:02:42 +08:00
|
|
|
|
p *interface{} // 注意这是一个指针
|
2018-04-12 14:09:33 +08:00
|
|
|
|
c byte // 层级分隔符,默认为"."
|
2018-07-01 00:38:35 +08:00
|
|
|
|
vc bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 21:44:36 +08:00
|
|
|
|
// 将变量转换为Json对象进行处理,该变量至少应当是一个map或者slice,否者转换没有意义
|
2019-01-12 23:36:22 +08:00
|
|
|
|
func New(value interface{}, unsafe...bool) *Json {
|
2018-09-13 18:40:04 +08:00
|
|
|
|
j := (*Json)(nil)
|
2018-09-14 17:02:07 +08:00
|
|
|
|
switch value.(type) {
|
2018-12-28 21:44:36 +08:00
|
|
|
|
case map[string]interface{}, []interface{}, nil:
|
2018-12-09 22:30:10 +08:00
|
|
|
|
j = &Json {
|
2018-12-28 21:44:36 +08:00
|
|
|
|
p : &value,
|
2018-09-14 17:02:07 +08:00
|
|
|
|
c : byte(gDEFAULT_SPLIT_CHAR),
|
2018-12-28 21:44:36 +08:00
|
|
|
|
vc : false ,
|
2018-09-14 17:02:07 +08:00
|
|
|
|
}
|
2018-12-28 21:44:36 +08:00
|
|
|
|
case string, []byte:
|
|
|
|
|
j, _ = LoadContent(gconv.Bytes(value))
|
|
|
|
|
default:
|
|
|
|
|
v := (interface{})(nil)
|
|
|
|
|
if m := gconv.Map(value); m != nil {
|
|
|
|
|
v = m
|
|
|
|
|
j = &Json {
|
|
|
|
|
p : &v,
|
|
|
|
|
c : byte(gDEFAULT_SPLIT_CHAR),
|
|
|
|
|
vc : false,
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
v = gconv.Interfaces(value)
|
|
|
|
|
j = &Json {
|
|
|
|
|
p : &v,
|
|
|
|
|
c : byte(gDEFAULT_SPLIT_CHAR),
|
|
|
|
|
vc : false,
|
|
|
|
|
}
|
2018-09-14 17:02:07 +08:00
|
|
|
|
}
|
2018-04-12 14:09:33 +08:00
|
|
|
|
}
|
2019-01-12 23:36:22 +08:00
|
|
|
|
j.mu = rwmutex.New(unsafe...)
|
2018-09-13 18:40:04 +08:00
|
|
|
|
return j
|
2018-04-12 14:09:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 13:56:01 +08:00
|
|
|
|
// 创建一个非并发安全的Json对象
|
|
|
|
|
func NewUnsafe(value...interface{}) *Json {
|
|
|
|
|
if len(value) > 0 {
|
2019-01-14 22:55:43 +08:00
|
|
|
|
return New(value[0], true)
|
2018-09-14 13:56:01 +08:00
|
|
|
|
}
|
2019-01-14 22:55:43 +08:00
|
|
|
|
return New(nil, true)
|
2018-09-14 13:56:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 17:48:37 +08:00
|
|
|
|
// 识别当前给定内容是否为JSON格式
|
|
|
|
|
func Valid (v interface{}) bool {
|
|
|
|
|
return json.Valid(gconv.Bytes(v))
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// 编码go变量为json字符串,并返回json字符串指针
|
2017-12-25 17:04:54 +08:00
|
|
|
|
func Encode (v interface{}) ([]byte, error) {
|
|
|
|
|
return json.Marshal(v)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解码字符串为interface{}变量
|
2017-12-25 17:04:54 +08:00
|
|
|
|
func Decode (b []byte) (interface{}, error) {
|
2017-11-23 10:21:28 +08:00
|
|
|
|
var v interface{}
|
2018-01-11 18:02:56 +08:00
|
|
|
|
if err := DecodeTo(b, &v); err != nil {
|
2017-12-13 17:35:43 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
} else {
|
|
|
|
|
return v, nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 15:26:28 +08:00
|
|
|
|
// 解析json字符串为go变量,注意第二个参数为指针(任意结构的变量)
|
2017-12-25 17:04:54 +08:00
|
|
|
|
func DecodeTo (b []byte, v interface{}) error {
|
|
|
|
|
return json.Unmarshal(b, v)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析json字符串为gjson.Json对象,并返回操作对象指针
|
2017-12-25 17:04:54 +08:00
|
|
|
|
func DecodeToJson (b []byte) (*Json, error) {
|
|
|
|
|
if v, err := Decode(b); err != nil {
|
2017-12-13 17:35:43 +08:00
|
|
|
|
return nil, err
|
2018-01-11 18:02:56 +08:00
|
|
|
|
} else {
|
2018-04-12 14:09:33 +08:00
|
|
|
|
return New(v), nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 15:26:28 +08:00
|
|
|
|
// 支持多种配置文件类型转换为json格式内容并解析为gjson.Json对象
|
2017-12-14 17:32:51 +08:00
|
|
|
|
func Load (path string) (*Json, error) {
|
2019-03-12 00:24:31 +08:00
|
|
|
|
return LoadContent(gfcache.GetBinContents(path), gfile.Ext(path))
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 21:44:36 +08:00
|
|
|
|
// 支持的配置文件格式:xml, json, yaml/yml, toml,
|
|
|
|
|
// 默认为自动识别,当无法检测成功时使用json解析。
|
|
|
|
|
func LoadContent(data []byte, dataType...string) (*Json, error) {
|
2018-01-19 16:19:48 +08:00
|
|
|
|
var err error
|
|
|
|
|
var result interface{}
|
2018-06-30 22:50:21 +08:00
|
|
|
|
t := "json"
|
|
|
|
|
if len(dataType) > 0 {
|
|
|
|
|
t = dataType[0]
|
2018-12-28 21:44:36 +08:00
|
|
|
|
} else {
|
|
|
|
|
if gregex.IsMatch(`<.+>.*</.+>`, data) {
|
|
|
|
|
t = "xml"
|
|
|
|
|
} else if gregex.IsMatch(`\w+\s*:\s*\w+`, data) {
|
|
|
|
|
t = "yml"
|
|
|
|
|
} else if gregex.IsMatch(`\w+\s*=\s*\w+`, data) {
|
|
|
|
|
t = "toml"
|
|
|
|
|
}
|
2018-06-30 22:50:21 +08:00
|
|
|
|
}
|
2018-01-19 16:19:48 +08:00
|
|
|
|
switch t {
|
2018-12-28 21:44:36 +08:00
|
|
|
|
case "xml", ".xml":
|
|
|
|
|
data, err = gxml.ToJson(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2018-01-20 11:09:27 +08:00
|
|
|
|
|
2018-12-28 21:44:36 +08:00
|
|
|
|
case "yml", "yaml", ".yml", ".yaml":
|
|
|
|
|
data, err = gyaml.ToJson(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "toml", ".toml":
|
|
|
|
|
data, err = gtoml.ToJson(data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2017-12-14 17:32:51 +08:00
|
|
|
|
}
|
2019-01-14 22:55:43 +08:00
|
|
|
|
if result == nil {
|
|
|
|
|
if err := json.Unmarshal(data, &result); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-12 14:09:33 +08:00
|
|
|
|
return New(result), nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 17:34:03 +08:00
|
|
|
|
// 设置自定义的层级分隔符号
|
|
|
|
|
func (j *Json) SetSplitChar(char byte) {
|
|
|
|
|
j.mu.Lock()
|
|
|
|
|
j.c = char
|
|
|
|
|
j.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 00:27:33 +08:00
|
|
|
|
// 设置是否执行层级冲突检查,当键名中存在层级符号时需要开启该特性,默认为关闭。
|
|
|
|
|
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
|
2018-04-12 17:34:03 +08:00
|
|
|
|
func (j *Json) SetViolenceCheck(check bool) {
|
|
|
|
|
j.mu.Lock()
|
|
|
|
|
j.vc = check
|
|
|
|
|
j.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// 将指定的json内容转换为指定结构返回,查找失败或者转换失败,目标对象转换为nil
|
2018-04-12 14:09:33 +08:00
|
|
|
|
// 注意第二个参数需要给的是**变量地址**
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetToVar(pattern string, v interface{}) error {
|
|
|
|
|
r := j.Get(pattern)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
if r != nil {
|
2017-12-13 17:35:43 +08:00
|
|
|
|
if t, err := Encode(r); err == nil {
|
|
|
|
|
return DecodeTo(t, v)
|
|
|
|
|
} else {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2017-11-23 10:21:28 +08:00
|
|
|
|
} else {
|
|
|
|
|
v = nil
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获得一个键值对关联数组/哈希表,方便操作,不需要自己做类型转换
|
|
|
|
|
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetMap(pattern string) map[string]interface{} {
|
|
|
|
|
result := j.Get(pattern)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
if result != nil {
|
|
|
|
|
if r, ok := result.(map[string]interface{}); ok {
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 11:07:44 +08:00
|
|
|
|
// 将检索值转换为Json对象指针返回
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetJson(pattern string) *Json {
|
|
|
|
|
result := j.Get(pattern)
|
2017-12-18 11:07:44 +08:00
|
|
|
|
if result != nil {
|
2018-04-12 14:09:33 +08:00
|
|
|
|
return New(result)
|
2017-12-18 11:07:44 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
|
|
|
|
|
// 注意,如果获取的值不存在,或者类型与json类型不匹配,那么将会返回nil
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetArray(pattern string) []interface{} {
|
|
|
|
|
result := j.Get(pattern)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
if result != nil {
|
|
|
|
|
if r, ok := result.([]interface{}); ok {
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回指定json中的string
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetString(pattern string) string {
|
|
|
|
|
return gconv.String(j.Get(pattern))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 00:27:33 +08:00
|
|
|
|
// 返回指定json中的strings(转换为[]string数组)
|
|
|
|
|
func (j *Json) GetStrings(pattern string) []string {
|
|
|
|
|
return gconv.Strings(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 10:05:55 +08:00
|
|
|
|
func (j *Json) GetInterfaces(pattern string) []interface{} {
|
|
|
|
|
return gconv.Interfaces(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 00:27:33 +08:00
|
|
|
|
func (j *Json) GetTime(pattern string, format ... string) time.Time {
|
|
|
|
|
return gconv.Time(j.Get(pattern), format...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetTimeDuration(pattern string) time.Duration {
|
|
|
|
|
return gconv.TimeDuration(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 11:09:16 +08:00
|
|
|
|
// 返回指定json中的bool(false:"", 0, false, off)
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetBool(pattern string) bool {
|
|
|
|
|
return gconv.Bool(j.Get(pattern))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetInt(pattern string) int {
|
|
|
|
|
return gconv.Int(j.Get(pattern))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 19:20:38 +08:00
|
|
|
|
func (j *Json) GetInt8(pattern string) int8 {
|
|
|
|
|
return gconv.Int8(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetInt16(pattern string) int16 {
|
|
|
|
|
return gconv.Int16(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetInt32(pattern string) int32 {
|
|
|
|
|
return gconv.Int32(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetInt64(pattern string) int64 {
|
|
|
|
|
return gconv.Int64(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 10:05:55 +08:00
|
|
|
|
func (j *Json) GetInts(pattern string) []int {
|
|
|
|
|
return gconv.Ints(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetUint(pattern string) uint {
|
|
|
|
|
return gconv.Uint(j.Get(pattern))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-23 19:20:38 +08:00
|
|
|
|
func (j *Json) GetUint8(pattern string) uint8 {
|
|
|
|
|
return gconv.Uint8(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetUint16(pattern string) uint16 {
|
|
|
|
|
return gconv.Uint16(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetUint32(pattern string) uint32 {
|
|
|
|
|
return gconv.Uint32(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (j *Json) GetUint64(pattern string) uint64 {
|
|
|
|
|
return gconv.Uint64(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetFloat32(pattern string) float32 {
|
|
|
|
|
return gconv.Float32(j.Get(pattern))
|
2017-12-31 11:09:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) GetFloat64(pattern string) float64 {
|
|
|
|
|
return gconv.Float64(j.Get(pattern))
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 10:05:55 +08:00
|
|
|
|
func (j *Json) GetFloats(pattern string) []float64 {
|
|
|
|
|
return gconv.Floats(j.Get(pattern))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-08 13:38:36 +08:00
|
|
|
|
// 将指定变量转换为struct对象(对象属性赋值)
|
|
|
|
|
func (j *Json) GetToStruct(pattern string, objPointer interface{}) error {
|
|
|
|
|
return gconv.Struct(j.Get(pattern), objPointer)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-26 17:42:14 +08:00
|
|
|
|
// 动态设置层级变量
|
|
|
|
|
func (j *Json) Set(pattern string, value interface{}) error {
|
|
|
|
|
return j.setValue(pattern, value, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 动态删除层级变量
|
|
|
|
|
func (j *Json) Remove(pattern string) error {
|
|
|
|
|
return j.setValue(pattern, nil, true)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-26 16:36:07 +08:00
|
|
|
|
// 根据pattern查找并设置数据
|
2018-01-23 16:40:48 +08:00
|
|
|
|
// 注意:
|
2018-04-12 14:09:33 +08:00
|
|
|
|
// 1、写入的value为nil且removed为true时,表示删除;
|
|
|
|
|
// 2、里面的层级处理比较复杂,逻辑较复杂的地方在于层级检索及节点创建,叶子赋值;
|
2018-01-26 17:42:14 +08:00
|
|
|
|
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
2018-04-12 14:09:33 +08:00
|
|
|
|
array := strings.Split(pattern, string(j.c))
|
|
|
|
|
length := len(array)
|
|
|
|
|
value = j.convertValue(value)
|
2018-01-23 18:23:05 +08:00
|
|
|
|
// 初始化判断
|
|
|
|
|
if *j.p == nil {
|
2018-05-19 00:02:19 +08:00
|
|
|
|
if gstr.IsNumeric(array[0]) {
|
2018-01-23 18:23:05 +08:00
|
|
|
|
*j.p = make([]interface{}, 0)
|
|
|
|
|
} else {
|
|
|
|
|
*j.p = make(map[string]interface{})
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-12 14:09:33 +08:00
|
|
|
|
var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级)
|
|
|
|
|
var pointer *interface{} = j.p // 当前操作层级项
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.Lock()
|
2018-04-12 17:22:12 +08:00
|
|
|
|
defer j.mu.Unlock()
|
2017-11-23 10:21:28 +08:00
|
|
|
|
for i:= 0; i < length; i++ {
|
|
|
|
|
switch (*pointer).(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
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 {
|
2018-01-26 16:36:07 +08:00
|
|
|
|
if removed && value == nil {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
goto done
|
2018-01-23 16:40:48 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
// 创建新节点
|
|
|
|
|
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
|
2018-04-12 17:22:12 +08:00
|
|
|
|
} else {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
// 创建map节点
|
|
|
|
|
var v interface{} = make(map[string]interface{})
|
|
|
|
|
pparent = j.setPointerWithValue(pointer, array[i], v)
|
2018-04-12 17:22:12 +08:00
|
|
|
|
pointer = &v
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
} else {
|
|
|
|
|
pparent = pointer
|
|
|
|
|
pointer = &v
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
}
|
2018-01-25 17:43:07 +08:00
|
|
|
|
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case []interface{}:
|
|
|
|
|
// 键名与当前指针类型不符合,需要执行**覆盖操作**
|
|
|
|
|
if !gstr.IsNumeric(array[i]) {
|
|
|
|
|
if i == length - 1 {
|
|
|
|
|
*pointer = map[string]interface{}{ array[i] : value }
|
|
|
|
|
} else {
|
|
|
|
|
var v interface{} = make(map[string]interface{})
|
|
|
|
|
*pointer = v
|
|
|
|
|
pparent = pointer
|
|
|
|
|
pointer = &v
|
2018-04-12 17:22:12 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
2018-04-12 17:34:03 +08:00
|
|
|
|
|
2018-12-09 22:30:10 +08:00
|
|
|
|
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 : ]...))
|
|
|
|
|
} 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)
|
|
|
|
|
}
|
2018-04-12 17:22:12 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
} else {
|
|
|
|
|
if gstr.IsNumeric(array[i + 1]) {
|
|
|
|
|
n, _ := strconv.Atoi(array[i + 1])
|
2018-04-12 17:22:12 +08:00
|
|
|
|
if len((*pointer).([]interface{})) > valn {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
(*pointer).([]interface{})[valn] = make([]interface{}, n + 1)
|
|
|
|
|
pparent = pointer
|
|
|
|
|
pointer = &(*pointer).([]interface{})[valn]
|
2018-01-22 17:55:12 +08:00
|
|
|
|
} else {
|
2018-04-12 17:34:03 +08:00
|
|
|
|
if removed && value == nil {
|
|
|
|
|
goto done
|
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
var v interface{} = make([]interface{}, n + 1)
|
2018-04-12 17:22:12 +08:00
|
|
|
|
pparent = j.setPointerWithValue(pointer, array[i], v)
|
|
|
|
|
pointer = &v
|
2018-01-26 16:36:07 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
} else {
|
|
|
|
|
var v interface{} = make(map[string]interface{})
|
|
|
|
|
pparent = j.setPointerWithValue(pointer, array[i], v)
|
|
|
|
|
pointer = &v
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
}
|
2018-01-25 17:43:07 +08:00
|
|
|
|
|
2018-04-12 17:22:12 +08:00
|
|
|
|
// 如果当前指针指向的变量不是引用类型的,
|
|
|
|
|
// 那么修改变量必须通过父级进行修改,即 pparent
|
2018-12-09 22:30:10 +08:00
|
|
|
|
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
|
2018-01-25 17:43:07 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
if pparent != nil {
|
|
|
|
|
pparent = j.setPointerWithValue(pparent, array[i - 1], s)
|
2018-01-25 17:43:07 +08:00
|
|
|
|
} else {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
*pointer = s
|
|
|
|
|
pparent = pointer
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var v interface{} = make(map[string]interface{})
|
|
|
|
|
if i == length - 1 {
|
|
|
|
|
v = map[string]interface{}{
|
|
|
|
|
array[i] : value,
|
2018-01-26 16:36:07 +08:00
|
|
|
|
}
|
2018-01-25 17:43:07 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
if pparent != nil {
|
|
|
|
|
pparent = j.setPointerWithValue(pparent, array[i - 1], v)
|
|
|
|
|
} else {
|
|
|
|
|
*pointer = v
|
|
|
|
|
pparent = pointer
|
|
|
|
|
}
|
|
|
|
|
pointer = &v
|
|
|
|
|
}
|
2018-04-12 17:22:12 +08:00
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-23 18:23:05 +08:00
|
|
|
|
done:
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-23 15:02:42 +08:00
|
|
|
|
// 数据结构转换,map参数必须转换为map[string]interface{},数组参数必须转换为[]interface{}
|
|
|
|
|
func (j *Json) convertValue(value interface{}) interface{} {
|
|
|
|
|
switch value.(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
return value
|
|
|
|
|
case []interface{}:
|
|
|
|
|
return value
|
|
|
|
|
default:
|
|
|
|
|
// 这里效率会比较低,当然比直接用反射也不会差到哪儿去
|
|
|
|
|
// 为了操作的灵活性,牺牲了一定的效率
|
|
|
|
|
b, _ := Encode(value)
|
|
|
|
|
v, _ := Decode(b)
|
|
|
|
|
return v
|
2018-01-23 15:02:42 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-26 16:36:07 +08:00
|
|
|
|
// 用于Set方法中,对指针指向的内存地址进行赋值
|
|
|
|
|
// 返回修改后的父级指针
|
|
|
|
|
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
|
|
|
|
|
switch (*pointer).(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
(*pointer).(map[string]interface{})[key] = value
|
|
|
|
|
return &value
|
|
|
|
|
case []interface{}:
|
|
|
|
|
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]
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
*pointer = value
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
2018-01-26 16:36:07 +08:00
|
|
|
|
return pointer
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-01 00:27:33 +08:00
|
|
|
|
// 根据约定字符串方式访问json解析数据,参数形如: "items.name.first", "list.0"; 当pattern为空时,表示获取所有数据;
|
|
|
|
|
// 返回的结果类型的interface{},因此需要自己做类型转换;
|
|
|
|
|
// 如果找不到对应节点的数据,返回nil;
|
|
|
|
|
func (j *Json) Get(pattern...string) interface{} {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-04-12 14:09:33 +08:00
|
|
|
|
|
2018-07-01 00:27:33 +08:00
|
|
|
|
queryPattern := ""
|
|
|
|
|
if len(pattern) > 0 {
|
|
|
|
|
queryPattern = pattern[0]
|
|
|
|
|
}
|
2018-04-12 14:09:33 +08:00
|
|
|
|
var result *interface{}
|
|
|
|
|
if j.vc {
|
2018-07-01 00:27:33 +08:00
|
|
|
|
result = j.getPointerByPattern(queryPattern)
|
2018-04-12 14:09:33 +08:00
|
|
|
|
} else {
|
2018-07-01 00:27:33 +08:00
|
|
|
|
result = j.getPointerByPatternWithoutSplitCharViolenceCheck(queryPattern)
|
2018-04-12 14:09:33 +08:00
|
|
|
|
}
|
|
|
|
|
if result != nil {
|
|
|
|
|
return *result
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 14:54:01 +08:00
|
|
|
|
// 判断锁给定pattern是否数据存在
|
|
|
|
|
func (j *Json) Contains(pattern...string) bool {
|
|
|
|
|
return j.Get(pattern...) != nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 17:02:07 +08:00
|
|
|
|
// 计算指定pattern的元素长度(pattern对应数据类型为map[string]interface{}/[]interface{}时有效)
|
|
|
|
|
func (j *Json) Len(pattern string) int {
|
|
|
|
|
p := j.getPointerByPattern(pattern)
|
|
|
|
|
if p != nil {
|
|
|
|
|
switch (*p).(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
return len((*p).(map[string]interface{}))
|
|
|
|
|
case []interface{}:
|
|
|
|
|
return len((*p).([]interface{}))
|
|
|
|
|
default:
|
|
|
|
|
return -1
|
2018-09-14 17:02:07 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-14 13:56:01 +08:00
|
|
|
|
// 指定pattern追加元素
|
|
|
|
|
func (j *Json) Append(pattern string, value interface{}) error {
|
2018-09-14 17:02:07 +08:00
|
|
|
|
length := j.Len(pattern)
|
|
|
|
|
if length != -1 {
|
|
|
|
|
return j.Set(fmt.Sprintf("%s.%d", pattern, length), value)
|
2018-09-14 13:56:01 +08:00
|
|
|
|
}
|
2018-09-14 17:02:07 +08:00
|
|
|
|
return errors.New(fmt.Sprintf("cannot find item for pattern: %s", pattern))
|
2018-09-14 13:56:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 14:09:33 +08:00
|
|
|
|
// 根据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;
|
|
|
|
|
// 这样检索的复杂度很高,主要是为了避免键名中存在分隔符号(默认为".")的情况,避免歧义。
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
|
|
|
|
index := len(pattern)
|
2018-04-10 15:36:35 +08:00
|
|
|
|
start := 0
|
2018-01-22 17:55:12 +08:00
|
|
|
|
length := 0
|
|
|
|
|
pointer := j.p
|
2018-04-10 15:36:35 +08:00
|
|
|
|
if index == 0 {
|
|
|
|
|
return pointer
|
|
|
|
|
}
|
2018-01-22 17:55:12 +08:00
|
|
|
|
for {
|
|
|
|
|
if r := j.checkPatternByPointer(pattern[start:index], pointer); r != nil {
|
|
|
|
|
length += index - start
|
|
|
|
|
if start > 0 {
|
|
|
|
|
length += 1
|
|
|
|
|
}
|
|
|
|
|
start = index + 1
|
|
|
|
|
index = len(pattern)
|
|
|
|
|
if length == len(pattern) {
|
|
|
|
|
return r
|
|
|
|
|
} else {
|
|
|
|
|
pointer = r
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-04-12 14:09:33 +08:00
|
|
|
|
// 查找下一个分割符号的索引位置
|
|
|
|
|
index = strings.LastIndexByte(pattern[start:index], j.c)
|
2018-01-23 15:43:16 +08:00
|
|
|
|
if index != -1 && length > 0 {
|
|
|
|
|
index += length + 1
|
|
|
|
|
}
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
if start >= index {
|
|
|
|
|
break
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-12 14:09:33 +08:00
|
|
|
|
// 层级检索,内部不执行分隔符冲突检查,检索效率会有所提高,但是冲突需要开发者自己根据自定义的分隔符来进行解决
|
|
|
|
|
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
|
2018-01-22 17:55:12 +08:00
|
|
|
|
// 注意这里返回的指针都是临时变量的内存地址
|
2018-04-12 14:09:33 +08:00
|
|
|
|
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
|
2018-01-22 17:55:12 +08:00
|
|
|
|
switch (*pointer).(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
if v, ok := (*pointer).(map[string]interface{})[key]; ok {
|
|
|
|
|
return &v
|
|
|
|
|
}
|
|
|
|
|
case []interface{}:
|
|
|
|
|
if gstr.IsNumeric(key) {
|
|
|
|
|
n, err := strconv.Atoi(key)
|
|
|
|
|
if err == nil && len((*pointer).([]interface{})) > n {
|
|
|
|
|
return &(*pointer).([]interface{})[n]
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
2018-12-09 22:30:10 +08:00
|
|
|
|
}
|
2018-01-22 17:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
return nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 转换为map[string]interface{}类型,如果转换失败,返回nil
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToMap() map[string]interface{} {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
switch (*(j.p)).(type) {
|
2018-12-28 21:44:36 +08:00
|
|
|
|
case map[string]interface{}:
|
|
|
|
|
return (*(j.p)).(map[string]interface{})
|
|
|
|
|
default:
|
|
|
|
|
return nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 转换为[]interface{}类型,如果转换失败,返回nil
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToArray() []interface{} {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
switch (*(j.p)).(type) {
|
2018-12-09 22:30:10 +08:00
|
|
|
|
case []interface{}:
|
|
|
|
|
return (*(j.p)).([]interface{})
|
|
|
|
|
default:
|
|
|
|
|
return nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToXml(rootTag...string) ([]byte, error) {
|
|
|
|
|
return gxml.Encode(j.ToMap(), rootTag...)
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
|
|
|
|
|
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToJson() ([]byte, error) {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return Encode(*(j.p))
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToJsonIndent() ([]byte, error) {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return json.MarshalIndent(*(j.p), "", "\t")
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToYaml() ([]byte, error) {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return gyaml.Encode(*(j.p))
|
2018-01-19 16:19:48 +08:00
|
|
|
|
}
|
2017-11-23 10:21:28 +08:00
|
|
|
|
|
2018-01-22 17:55:12 +08:00
|
|
|
|
func (j *Json) ToToml() ([]byte, error) {
|
2018-01-23 15:02:42 +08:00
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-01-22 17:55:12 +08:00
|
|
|
|
return gtoml.Encode(*(j.p))
|
2018-01-20 11:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-29 21:33:47 +08:00
|
|
|
|
// 转换为指定的struct对象
|
|
|
|
|
func (j *Json) ToStruct(o interface{}) error {
|
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
2018-10-08 13:38:36 +08:00
|
|
|
|
return gconv.Struct(*(j.p), o)
|
2018-09-14 13:56:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 打印Json对象
|
|
|
|
|
func (j *Json) Dump() error {
|
|
|
|
|
j.mu.RLock()
|
|
|
|
|
defer j.mu.RUnlock()
|
|
|
|
|
if b, err := j.ToJsonIndent(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println(string(b))
|
|
|
|
|
}
|
|
|
|
|
return nil
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|