Compare commits

..

20 Commits

Author SHA1 Message Date
630d8fdb43 add Instance function for gcfg 2019-04-03 09:59:15 +08:00
c4c7e6caf4 gofmt 2019-04-03 00:03:46 +08:00
8a8fea1257 v1.5.23 2019-04-02 23:40:20 +08:00
0e0f297a3f add consurrent safety parameter for gjson.Load/LoadContent; fulfil data type auto-check logics for gjson.LoadConetnt; update comment of gjson/gparser 2019-04-02 23:33:27 +08:00
fd2c0f2b24 Merge pull request #59 from wenzi1/master
修复gxml字符集转换的并发安全问题
2019-04-02 17:43:19 +08:00
ecc6e3888d up 2019-04-02 17:10:21 +08:00
47c073aaf3 add Clear function for gcfg; mark Reload function of gcfg as deprecated; update unit test for gins 2019-04-02 16:08:46 +08:00
07476a4349 add instance management feature for gdb/gredis; add customized configuration content management feature for gcfg; update gjson for data content type check 2019-04-02 14:37:46 +08:00
817148f3a1 增加单元测试 2019-04-01 18:32:37 +08:00
bd4271cd8c 增加gxml的单元测试 2019-03-31 23:36:11 +08:00
b1804fc346 TODO updates; version updates 2019-03-31 21:13:11 +08:00
a3886c2179 fix issue in RemoteAddr of gudp.Conn 2019-03-31 20:58:31 +08:00
f258b5bf1c change logging level from error to debug in route regitsry for struct for ghttp package; handle possible overrange usage of r.URL.Path in ghttp.Server 2019-03-31 20:52:30 +08:00
a05361011f 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换 2019-03-30 23:53:42 +08:00
6b34a77251 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换 2019-03-30 23:39:07 +08:00
afb1adee3d version updates 2019-03-28 17:58:33 +08:00
22fa7a37f3 add UseNumber support for gjson; add more unit test cases for gjson 2019-03-28 17:51:05 +08:00
6a58bfc574 add stdout printing support for ghttp.Server logging 2019-03-28 09:34:16 +08:00
64124c60fc add fatal error when no router set or statis feature enabled for ghttp.Server 2019-03-27 16:23:35 +08:00
9a0066de62 add file/folder search support for gcfg/gview in order of envpath/pwdpath/binpath/mainpath; add gfile.Search function 2019-03-27 11:48:53 +08:00
348 changed files with 7595 additions and 6058 deletions

38
TODO.MD
View File

@ -1,23 +1,16 @@
# ON THE WAY
1. 增加图形验证码支持,至少支持数字和英文字母;
1. 增加热编译工具,提高开发环境的开发/测试效率媲美PHP开发效率
1. 增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射
1. ghttp.Response增加输出内容后自动退出当前请求机制不需要用户手动return参考beego如何实现
1. Cookie&Session数据池化处理
1. ghttp.Client增加proxy特性
1. gtime增加对时区转换的封装并简化失去转换时对类似+80500时区的支持
1. orm增加sqlite对Save方法的支持(去掉触发器语句);
1. ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps)
1. ghttp路由功能增加分组路由特性
1. ghttp增加返回数据压缩机制
1. gview中的template标签失效问题
1. gfile文件stat信息使用gfsnotify进行缓存更新改进
1. ghttp.Server增加proxy功能特性本地proxy和远程proxy本地即将路由规则映射远程即反向代理
1. gjson对大json数据的解析效率问题
1. ghttp增加route name特性并同时支持backend和template(提供内置函数)引用可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上
1. ghttp.Client自动Close机制
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则最好做成链式操作
1. 检查ghttp.Server超时问题
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节)
1. ghttp.Request增加对输入参数的自动HtmlEncode机制
1. 常量命名风格根据golint进行修改
@ -36,29 +29,17 @@
- https://github.com/Masterminds/sprig
1. gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
1. gtcp提供简便的包发送/接收方法(SendPkg/RecvPkg)以解决常见的TCP通信粘包问题并完善文档参考https://www.cnblogs.com/kex1n/p/6502002.html
1. gfile对于文件的读写强行使用了gfpool在某些场景下不合适需要考虑剥离开并为开发者提供单独的指针池文件操作特性
1. 路由增加不区分大小写得匹配方式;
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
1. 改进WebServer获取POST参数处理逻辑当提交非form数据时例如json数据针对某些方法可以直接解析
1. WebServer增加可选择的路由覆盖配置默认情况下不覆盖
1. gkafka这个包比较重未来从框架中剥离出来
1. grpool性能压测结果变慢的问题
1. 增加jumplist的数据结构容器
1. DelayQueue/PriorityQueue
1. gconv针对struct的转换增加json tag支持gconv.Map默认也支持json tag, 完善开发文档;
1. 增加SO_REUSEPORT的支持
1. 权限管理模块;
1. 从ghttp中剥离SESSION功能构成单独的模块gsession
1. 改进gproc进程间通信处理逻辑提高稳定性以应对进程间大批量的数据发送/接收;
1. gdb的Data方法支持struct参数传入
1. gfcache依旧使用gcache作为缓存控制对象不要使用gmap
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
1. 更新跨域请求CORS相关功能文档
1. ghttp的热重启的本地进程端口监听在不使用该特性时默认关闭掉
1. gcfg包目前允许添加重复的目录路径需要在SetPath/AddPath时判断重复性不能添加重复的路径
1. gdb执行数据写入时如果参数为struct/[]struct自动映射与表字段对应关系不再使用gconv标签标识
1. gtcp增加对TLS加密通信的支持
@ -119,4 +100,19 @@
1. gform对于MySQL字段类型为datetime类型的时区问题分析
1. 改进证书打开失败时的WebServer错误提示前置HOOK校验后关闭后续的HOOK逻辑执行
1. 目前WebServer的HOOK是按照优先级执行的需要增加覆盖特性
1. 更新跨域请求CORS相关功能文档
1. ghttp.Response增加输出内容后自动退出当前请求机制不需要用户手动return参考beego如何实现
1. gcfg包目前允许添加重复的目录路径需要在SetPath/AddPath时判断重复性不能添加重复的路径
1. gdb执行数据写入时如果参数为struct/[]struct自动映射与表字段对应关系不再使用gconv标签标识
1. gdb的Data方法支持struct参数传入
1. gfcache依旧使用gcache作为缓存控制对象不要使用gmap
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
1. gconv针对struct的转换增加json tag支持gconv.Map默认也支持json tag, 完善开发文档;
1. 增加SO_REUSEPORT的支持
1. gkafka这个包比较重未来从框架中剥离出来
1. str_ireplace: http://php.net/manual/en/function.str-ireplace.php
1. strpos/stripos/strrpos/strripos: http://php.net/manual/en/function.stripos.php
1. gfile对于文件的读写强行使用了gfpool在某些场景下不合适需要考虑剥离开并为开发者提供单独的指针池文件操作特性
1. ghttp.Client自动Close机制
1. ghttp路由功能增加分组路由特性
1. 增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射

View File

@ -14,6 +14,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
@ -155,22 +156,26 @@ const (
gDEFAULT_BATCH_NUM = 10
// 默认的连接池连接存活时间(秒)
gDEFAULT_CONN_MAX_LIFE_TIME = 30
)
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
)
// 使用默认/指定分组配置进行连接数据库集群配置项default
func New(groupName ...string) (db DB, err error) {
group := config.d
if len(groupName) > 0 {
group = groupName[0]
func New(name...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
config.RLock()
defer config.RUnlock()
configs.RLock()
defer configs.RUnlock()
if len(config.c) < 1 {
if len(configs.config) < 1 {
return nil, errors.New("empty database configuration")
}
if _, ok := config.c[group]; ok {
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase {
group : group,
@ -204,9 +209,25 @@ func New(groupName ...string) (db DB, err error) {
}
}
// 获得数据库操作对象单例
func Instance(name...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
}
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
if list, ok := config.c[group]; ok {
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
@ -319,17 +340,17 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
return
}
// 切换操作的数据库(注意该切换是全局的)
// 切换当前数据库对象操作的数据库。
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
}
// 创建底层数据库master链接对象
// 创建底层数据库master链接对象
func (bs *dbBase) Master() (*sql.DB, error) {
return bs.getSqlDb(true)
}
// 创建底层数据库slave链接对象
// 创建底层数据库slave链接对象
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false)
}

View File

@ -17,14 +17,7 @@ const (
DEFAULT_GROUP_NAME = "default" // 默认配置名称
)
// 数据库配置包内对象
var config struct {
sync.RWMutex
c Config // 数据库配置
d string // 默认数据库分组名称
}
// 数据库配置
// 数据库分组配置
type Config map[string]ConfigGroup
// 数据库集群配置
@ -41,12 +34,19 @@ type ConfigNode struct {
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf8)编码,默认为 utf8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
Linkinfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
MaxIdleConnCount int // (可选)连接池最大限制的连接数
MaxOpenConnCount int // (可选)连接池最大打开的连接数
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
}
// 数据库配置包内对象
var configs struct {
sync.RWMutex // 并发安全互斥锁
config Config // 数据库分组配置
defaultGroup string // 默认数据库分组名称
}
// 数据库集群配置示例,支持主从处理,多数据库集群支持
/*
var DatabaseConfiguration = Config {
@ -80,29 +80,32 @@ var DatabaseConfiguration = Config {
// 包初始化
func init() {
config.c = make(Config)
config.d = DEFAULT_GROUP_NAME
configs.config = make(Config)
configs.defaultGroup = DEFAULT_GROUP_NAME
}
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
func SetConfig (c Config) {
config.Lock()
defer config.Unlock()
config.c = c
func SetConfig (config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config = config
}
// 添加数据库服务器集群配置
func AddConfigGroup (group string, nodes ConfigGroup) {
config.Lock()
config.c[group] = nodes
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = nodes
}
// 添加一台数据库服务器配置
func AddConfigNode (group string, node ConfigNode) {
config.Lock()
config.c[group] = append(config.c[group], node)
config.Unlock()
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
}
// 添加默认链接的一台数据库服务器配置
@ -117,16 +120,25 @@ func AddDefaultConfigGroup (nodes ConfigGroup) {
// 添加一台数据库服务器配置
func GetConfig (group string) ConfigGroup {
config.RLock()
defer config.RUnlock()
return config.c[group]
configs.RLock()
defer configs.RUnlock()
return configs.config[group]
}
// 设置默认链接的数据库链接配置项(默认是 default)
func SetDefaultGroup (groupName string) {
config.Lock()
config.d = groupName
config.Unlock()
func SetDefaultGroup (name string) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.defaultGroup = name
}
// 获取默认链接的数据库链接配置项(默认是 default)
func GetDefaultGroup() string {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
return configs.defaultGroup
}
// 设置数据库连接池中空闲链接的大小
@ -147,8 +159,8 @@ func (bs *dbBase) SetConnMaxLifetime(n int) {
// 节点配置转换为字符串
func (node *ConfigNode) String() string {
if node.Linkinfo != "" {
return node.Linkinfo
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset,

View File

@ -30,8 +30,8 @@ type dbMssql struct {
// 创建SQL操作对象
func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
source := ""
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name)

View File

@ -20,8 +20,8 @@ type dbMysql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)

View File

@ -30,8 +30,8 @@ type dbOracle struct {
// 创建SQL操作对象
func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name)
}

View File

@ -26,8 +26,8 @@ type dbPgsql struct {
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbPgsql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
}

View File

@ -24,8 +24,8 @@ type dbSqlite struct {
func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.Linkinfo != "" {
source = config.Linkinfo
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = config.Name
}

View File

@ -0,0 +1,28 @@
// Copyright 2019 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 gdb_test
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
_, err := gdb.Instance("none")
gtest.AssertNE(err, nil)
db, err := gdb.Instance()
gtest.Assert(err, nil)
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
}

View File

@ -4,10 +4,11 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gredis provides client for redis server.
// Package gredis provides convenient client for redis server.
//
// Redis客户端.
// Redis中文手册文档请参考http://redisdoc.com/ , Redis官方命令请参考https://redis.io/commands
// Redis中文手册请参考http://redisdoc.com/
// Redis官方命令请参考https://redis.io/commands
package gredis
import (
@ -24,8 +25,9 @@ const (
// Redis客户端(管理连接池)
type Redis struct {
pool *redis.Pool
config Config
pool *redis.Pool // 底层连接池
group string // 配置分组
config Config // 配置对象
}
// Redis连接对象(连接池中的单个连接)
@ -48,13 +50,17 @@ type PoolStats struct {
redis.PoolStats
}
// 连接池map
var pools = gmap.NewStringInterfaceMap()
var (
// 单例对象Map
instances = gmap.NewStringInterfaceMap()
// 连接池Map
pools = gmap.NewStringInterfaceMap()
)
// New creates a redis client object with given configuration.
// Redis client maintains a connection pool automatically.
//
// 创建redis操作对象.
// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。
func New(config Config) *Redis {
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
@ -96,11 +102,41 @@ func New(config Config) *Redis {
}
}
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// return redis instance with default group.
//
// 获取指定分组名称的Redis单例对象底层根据配置信息公用的连接池连接池单例
func Instance(name...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It always not necessary to call Close manually.
//
// 关闭redis管理对象将会关闭底层的连接池。
// 往往没必要手动调用,跟随进程销毁即可。
func (r *Redis) Close() error {
if r.group != "" {
// 如果是单例对象那么需要从单例对象Map中删除
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
}

View File

@ -0,0 +1,69 @@
// Copyright 2019 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 gredis
import "github.com/gogf/gf/g/container/gmap"
const (
// 默认分组名称
DEFAULT_GROUP_NAME = "default"
)
var (
// 分组配置
configs = gmap.NewStringInterfaceMap()
)
// SetConfig sets the global configuration for specified group.
// If <name> is not passed, it sets configuration for the default group name.
//
// 设置全局分组配置name为非必需参数默认为默认分组名称。
func SetConfig(config Config, name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Set(group, config)
instances.Remove(group)
}
// GetConfig returns the global configuration with specified group.
// If <group> is not passed, it returns configuration of the default group name.
//
// 获取指定全局分组配置group为非必需参数默认为默认分组名称。
func GetConfig(name...string) (config Config, ok bool) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
if v := configs.Get(group); v != nil {
return v.(Config), true
}
return Config{}, false
}
// RemoveConfig removes the global configuration with specified group.
// If <name> is not passed, it removes configuration of the default group name.
//
// 删除指定全局分组配置name为非必需参数默认为默认分组名称。
func RemoveConfig(name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Remove(group)
instances.Remove(group)
}
// ClearConfig removes all configurations and instances of redis.
//
// 清除所有的配置内容。
func ClearConfig() {
configs.Clear()
instances.Clear()
}

View File

@ -99,6 +99,33 @@ func Test_Conn(t *testing.T) {
conn := redis.Conn()
defer conn.Close()
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))
_, err = conn.Do("DEL", "k")
gtest.Assert(err, nil)
r, err = conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, nil)
})
}
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
group := "my-test"
gredis.SetConfig(config, group)
defer gredis.RemoveConfig(group)
redis := gredis.Instance(group)
defer redis.Close()
conn := redis.Conn()
defer conn.Close()
_, err := conn.Do("SET", "k", "v")
gtest.Assert(err, nil)
r, err := conn.Do("GET", "k")
gtest.Assert(err, nil)
gtest.Assert(r, []byte("v"))

View File

@ -51,5 +51,5 @@ func ToUTF8(charset string, src string) (dst string, err error) {
// UTF8转指定字符集
func UTF8To(charset string, src string) (dst string, err error) {
return Convert(charset, "UTF-8", src)
return Convert(charset, "UTF-8", src)
}

View File

@ -1,10 +1,16 @@
package gcharset
// Copyright 2018 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 gcharset_test
import (
"github.com/gogf/gf/g/encoding/gcharset"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
@ -52,7 +58,7 @@ var testData = []struct {
func TestDecode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert("UTF-8", data.otherEncoding, data.other)
str, err := gcharset.Convert("UTF-8", data.otherEncoding, data.other)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -68,7 +74,7 @@ func TestDecode(t *testing.T) {
func TestEncode(t *testing.T) {
for _, data := range testData {
str := ""
str, err := Convert(data.otherEncoding, "UTF-8", data.utf8)
str, err := gcharset.Convert(data.otherEncoding, "UTF-8", data.utf8)
if err != nil {
t.Errorf("Could not create decoder for %v", err)
continue
@ -86,7 +92,7 @@ func TestConvert(t *testing.T) {
dstCharset := "gbk"
dst := "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed"
str, err := Convert(dstCharset, srcCharset, src)
str, err := gcharset.Convert(dstCharset, srcCharset, src)
if err != nil {
t.Errorf("convert error. %v", err)
return

View File

@ -4,18 +4,19 @@
// 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 quite flexible and useful API for JSON/XML/YAML/TOML content handling.
// Package gjson provides convenient API for JSON/XML/YAML/TOML data handling.
package gjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/g/encoding/gtoml"
"github.com/gogf/gf/g/encoding/gxml"
"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"
@ -26,32 +27,38 @@ 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否者转换没有意义
func New(value interface{}, unsafe...bool) *Json {
// 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 value.(type) {
switch data.(type) {
case map[string]interface{}, []interface{}, nil:
j = &Json {
p : &value,
p : &data,
c : byte(gDEFAULT_SPLIT_CHAR),
vc : false ,
}
case string, []byte:
j, _ = LoadContent(gconv.Bytes(value))
j, _ = LoadContent(gconv.Bytes(data))
default:
v := (interface{})(nil)
if m := gconv.Map(value); m != nil {
if m := gconv.Map(data); m != nil {
v = m
j = &Json {
p : &v,
@ -59,7 +66,7 @@ func New(value interface{}, unsafe...bool) *Json {
vc : false,
}
} else {
v = gconv.Interfaces(value)
v = gconv.Interfaces(data)
j = &Json {
p : &v,
c : byte(gDEFAULT_SPLIT_CHAR),
@ -71,114 +78,130 @@ func New(value interface{}, unsafe...bool) *Json {
return j
}
// 创建一个非并发安全的Json对象
func NewUnsafe(value...interface{}) *Json {
if len(value) > 0 {
return New(value[0], true)
// NewUnsafe creates a un-concurrent-safe Json object.
func NewUnsafe(data...interface{}) *Json {
if len(data) > 0 {
return New(data[0], true)
}
return New(nil, true)
}
// 识别当前给定内容是否为JSON格式
func Valid (v interface{}) bool {
return json.Valid(gconv.Bytes(v))
// Valid checks whether <data> is a valid JSON data type.
func Valid(data interface{}) bool {
return json.Valid(gconv.Bytes(data))
}
// 编码go变量为json字符串并返回json字符串指针
func Encode (v interface{}) ([]byte, error) {
return json.Marshal(v)
// Encode encodes <value> to JSON data type of bytes.
func Encode(value interface{}) ([]byte, error) {
return json.Marshal(value)
}
// 解码字符串为interface{}变量
func Decode (b []byte) (interface{}, error) {
var v interface{}
if err := DecodeTo(b, &v); err != nil {
// 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 {
return nil, err
} else {
return v, nil
return value, nil
}
}
// 解析json字符串为go变量注意第二个参数为指针(任意结构的变量)
func DecodeTo (b []byte, v interface{}) error {
return json.Unmarshal(b, v)
// 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 (b []byte) (*Json, error) {
if v, err := Decode(b); err != nil {
// 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 []byte, 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 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"
}
return nil, errors.New("unsupported data type")
}
// convert to json type data
switch t {
case "xml", ".xml":
data, err = gxml.ToJson(data)
if err != nil {
return nil, err
}
case "json", ".json":
// ok
case "xml", ".xml":
// TODO UseNumber
b, err = gxml.ToJson(b)
case "yml", "yaml", ".yml", ".yaml":
data, err = gyaml.ToJson(data)
if err != nil {
return nil, err
}
case "yml", "yaml", ".yml", ".yaml":
// TODO UseNumber
b, err = gyaml.ToJson(b)
case "toml", ".toml":
data, err = gtoml.ToJson(data)
if err != nil {
return nil, err
}
case "toml", ".toml":
// TODO UseNumber
b, err = gtoml.ToJson(b)
default:
err = errors.New("nonsupport type " + t)
}
if err != nil {
return nil, err
}
if result == nil {
if err := json.Unmarshal(data, &result); err != nil {
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
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 {
@ -193,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 {
@ -214,28 +236,39 @@ func (j *Json) GetJson(pattern string) *Json {
return nil
}
// 获得一个数组[]interface{},方便操作,不需要自己做类型转换
// 注意如果获取的值不存在或者类型与json类型不匹配那么将会返回nil
func (j *Json) GetArray(pattern string) []interface{} {
result := j.Get(pattern)
if result != nil {
if r, ok := result.([]interface{}); ok {
return r
// 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 {
jsons := make([]*Json, len(array))
for i := 0; i < len(array); i++ {
jsons[i] = New(array[i], !j.mu.IsSafe())
}
return jsons
}
return nil
}
// 返回指定json中的string
// 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))
}
// 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))
}
@ -248,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))
}
@ -309,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)
@ -346,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{}:
@ -503,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
@ -511,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{}:
@ -536,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()
@ -559,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 {
@ -581,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 {
@ -594,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)
@ -603,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)
@ -640,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
@ -653,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)
@ -677,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{}:
@ -696,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()
@ -708,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
}
}
@ -724,42 +760,73 @@ func (j *Json) ToXml(rootTag...string) ([]byte, error) {
return gxml.Encode(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlString(rootTag...string) (string, error) {
b, e := j.ToXml(rootTag...)
return string(b), e
}
func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) {
return gxml.EncodeWithIndent(j.ToMap(), rootTag...)
}
func (j *Json) ToXmlIndentString(rootTag...string) (string, error) {
b, e := j.ToXmlIndent(rootTag...)
return string(b), e
}
func (j *Json) ToJson() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return Encode(*(j.p))
}
func (j *Json) ToJsonString() (string, error) {
b, e := j.ToJson()
return string(b), e
}
func (j *Json) ToJsonIndent() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return json.MarshalIndent(*(j.p), "", "\t")
}
func (j *Json) ToJsonIndentString() (string, error) {
b, e := j.ToJsonIndent()
return string(b), e
}
func (j *Json) ToYaml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gyaml.Encode(*(j.p))
}
func (j *Json) ToYamlString() (string, error) {
b, e := j.ToYaml()
return string(b), e
}
func (j *Json) ToToml() ([]byte, error) {
j.mu.RLock()
defer j.mu.RUnlock()
return gtoml.Encode(*(j.p))
}
// 转换为指定的struct对象
func (j *Json) ToStruct(o interface{}) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.Struct(*(j.p), o)
func (j *Json) ToTomlString() (string, error) {
b, e := j.ToToml()
return string(b), e
}
// 打印Json对象
// 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), objPointer)
}
// Dump prints current Json object with more manually readable.
func (j *Json) Dump() error {
j.mu.RLock()
defer j.mu.RUnlock()

View File

@ -0,0 +1,253 @@
// 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 gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"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 := gjson.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 := gjson.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_Valid(t *testing.T) {
data1 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
data2 := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]`)
gtest.Case(t, func() {
gtest.Assert(gjson.Valid(data1), true)
gtest.Assert(gjson.Valid(data2), false)
})
}
func Test_Encode(t *testing.T) {
value := g.Slice{1, 2, 3}
gtest.Case(t, func() {
b, err := gjson.Encode(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() {
v, err := gjson.Decode(data)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
var v interface{}
err := gjson.DecodeTo(data, &v)
gtest.Assert(err, nil)
gtest.Assert(v, g.Map{
"n" : 123456789,
"a" : g.Slice{1, 2, 3},
"m" : g.Map{
"k" : "v",
},
})
})
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(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)
})
}
func Test_SplitChar(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
j.SetSplitChar(byte('#'))
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_ViolenceCheck(t *testing.T) {
data := []byte(`{"m":{"a":[1,2,3], "v1.v2":"4"}}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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_GetJson(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, nil)
j2 := j.GetJson("m")
gtest.AssertNE(j2, nil)
gtest.Assert(j2.Get("k"), "v")
gtest.Assert(j2.Get("a"), nil)
gtest.Assert(j2.Get("n"), nil)
})
}
func Test_GetArray(t *testing.T) {
data := []byte(`{"n":123456789, "m":{"k":"v"}, "a":[1,2,3]}`)
gtest.Case(t, func() {
j, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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, err := gjson.DecodeToJson(data)
gtest.Assert(err, 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 := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.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 := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View 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 gjson_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"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 := gjson.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 := gjson.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 := gjson.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 := gjson.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 := gjson.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 := gjson.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 := 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"
[m]
k = "v"
`)
// TOML
gtest.Case(t, func() {
j, err := gjson.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 := gjson.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 := gjson.LoadContent(data)
gtest.Assert(err, nil)
gtest.Assert(j.Get("i"), "123456789")
})
}

View File

@ -8,9 +8,7 @@ package gjson_test
import (
"bytes"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
@ -174,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 {
@ -230,51 +228,3 @@ func Test_Set14(t *testing.T) {
t.Error(err)
}
}
func Test_Len(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a.b", 1)
p.Append("a.c", 2)
gtest.Assert(p.Len("a"), 2)
})
gtest.Case(t, func() {
p := gjson.New(nil)
p.Set("a", 1)
gtest.Assert(p.Len("a"), -1)
})
}
func Test_Append(t *testing.T) {
gtest.Case(t, func() {
p := gjson.New(nil)
p.Append("a", 1)
p.Append("a", 2)
gtest.Assert(p.Get("a"), g.Slice{1, 2})
})
gtest.Case(t, func() {
p := gjson.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 := gjson.New(nil)
p.Set("a", 1)
err := p.Append("a", 2)
gtest.AssertNE(err, nil)
gtest.Assert(p.Get("a"), 1)
})
}

View File

@ -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)
}

View 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)
})
}

View 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")
})
}

View File

@ -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)
}
}

View File

@ -5,75 +5,80 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gxml provides accessing and converting for XML content.
//
// XML数据格式解析。
package gxml
import (
"github.com/gogf/gf/third/github.com/clbanning/mxj"
"encoding/xml"
"io"
"fmt"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/third/github.com/axgle/mahonia"
"errors"
"fmt"
"github.com/gogf/gf/third/github.com/clbanning/mxj"
"strings"
)
// 将XML内容解析为map变量
func Decode(xmlbyte []byte) (map[string]interface{}, error) {
prepare(xmlbyte)
return mxj.NewMapXml(xmlbyte)
func Decode(content []byte) (map[string]interface{}, error) {
res, err := convert(content)
if err != nil {
return nil, err
}
return mxj.NewMapXml(res)
}
// 将map变量解析为XML格式内容
func Encode(v map[string]interface{}, rootTag...string) ([]byte, error) {
return mxj.Map(v).Xml(rootTag...)
func Encode(v map[string]interface{}, rootTag ...string) ([]byte, error) {
return mxj.Map(v).Xml(rootTag...)
}
func EncodeWithIndent(v map[string]interface{}, rootTag...string) ([]byte, error) {
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
func EncodeWithIndent(v map[string]interface{}, rootTag ...string) ([]byte, error) {
return mxj.Map(v).XmlIndent("", "\t", rootTag...)
}
// XML格式内容直接转换为JSON格式内容
func ToJson(xmlbyte []byte) ([]byte, error) {
prepare(xmlbyte)
mv, err := mxj.NewMapXml(xmlbyte)
func ToJson(content []byte) ([]byte, error) {
res, err := convert(content)
if err != nil {
fmt.Println("convert error. ", err)
return nil, err
}
mv, err := mxj.NewMapXml(res)
if err == nil {
return mv.Json()
} else {
return nil, err
}
return mv.Json()
} else {
return nil, err
}
}
// XML字符集预处理
// @author wenzi1
// @date 20180604
func prepare(xmlbyte []byte) error {
// @date 20180604 修复并发安全问题,改为如果非UTF8字符集则先做字符集转换
func convert(xmlbyte []byte) (res []byte, err error) {
patten := `<\?xml.*encoding\s*=\s*['|"](.*?)['|"].*\?>`
charsetReader := func(charset string, input io.Reader) (io.Reader, error) {
reader := mahonia.GetCharset(charset)
if reader == nil {
return nil, errors.New(fmt.Sprintf("not support charset:%s", charset))
}
return reader.NewDecoder().NewReader(input), nil
}
matchStr, err := gregex.MatchString(patten, string(xmlbyte))
if err != nil {
return err
return nil, err
}
xmlEncode := "UTF-8"
if len(matchStr) == 2 {
xmlEncode = matchStr[1]
xmlEncode = matchStr[1]
}
charset := mahonia.GetCharset(xmlEncode)
if charset == nil {
return errors.New(fmt.Sprintf("not support charset:%s", xmlEncode))
s := mahonia.GetCharset(xmlEncode)
if s == nil {
return nil, fmt.Errorf("not support charset:%s\n", xmlEncode)
}
if !strings.EqualFold(charset.Name, "UTF-8") {
mxj.CustomDecoder = &xml.Decoder{Strict : false, CharsetReader : charsetReader}
res, err = gregex.Replace(patten, []byte(""), []byte(xmlbyte))
if err != nil {
return nil, err
}
return nil
}
if !strings.EqualFold(s.Name, "UTF-8") {
res = []byte(s.NewDecoder().ConvertString(string(res)))
}
return res, nil
}

View File

@ -0,0 +1,140 @@
// 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 gxml_test
import (
"bytes"
"github.com/gogf/gf/g/encoding/gcharset"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gxml"
"strings"
"testing"
)
var testData = []struct {
utf8, other, otherEncoding string
}{
{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
}
func buildXml(charset string, str string) (string, string) {
head := `<?xml version="1.0" encoding="UTF-8"?>`
srcXml := strings.Replace(head, "UTF-8", charset, -1)
srcParser := gparser.New(nil)
srcParser.Set("name", str)
srcParser.Set("age", "12")
s, err := srcParser.ToXml()
if err != nil {
return "", ""
}
srcXml = srcXml + string(s)
srcXml, err = gcharset.UTF8To(charset, srcXml)
if err != nil {
return "", ""
}
dstXml := head + string(s)
return srcXml, dstXml
}
//测试XML中字符集的转换
func Test_XmlToJson(t *testing.T) {
for _, v := range testData {
srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
if len(srcXml) == 0 && len(dstXml) == 0 {
t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
}
srcJson, err := gxml.ToJson([]byte(srcXml))
if err != nil {
t.Errorf("gxml.ToJson error. %s", srcXml)
}
dstJson, err := gxml.ToJson([]byte(dstXml))
if err != nil {
t.Errorf("dstXml to json error. %s", dstXml)
}
if bytes.Compare(srcJson, dstJson) != 0 {
t.Errorf("convert to json error. srcJson:%s, dstJson:%s", string(srcJson), string(dstJson))
}
}
}
func Test_Decode(t *testing.T) {
for _, v := range testData {
srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
if len(srcXml) == 0 && len(dstXml) == 0 {
t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
}
srcMap, err := gxml.Decode([]byte(srcXml))
if err != nil {
t.Errorf("gxml.Decode error. %s", srcXml)
}
dstMap, err := gxml.Decode([]byte(dstXml))
if err != nil {
t.Errorf("gxml decode error. %s", dstXml)
}
s := srcMap["doc"].(map[string]interface{})
d := dstMap["doc"].(map[string]interface{})
for kk, vv := range s {
if vv.(string) != d[kk].(string) {
t.Errorf("convert to map error. src:%v, dst:%v", vv, d[kk])
}
}
}
}
func Test_Encode(t *testing.T) {
m := make(map[string]interface{})
v := map[string]interface{}{
"string": "hello world",
"int": 123,
"float": 100.92,
"bool": true,
}
m["root"] = interface{}(v)
xmlStr, err := gxml.Encode(m)
if err != nil {
t.Errorf("encode error.")
}
t.Logf("%s\n", string(xmlStr))
res := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string></root>`
if string(xmlStr) != res {
t.Errorf("encode error. result: [%s], expect:[%s]", string(xmlStr), res)
}
}
func Test_EncodeIndent(t *testing.T) {
m := make(map[string]interface{})
v := map[string]interface{}{
"string": "hello world",
"int": 123,
"float": 100.92,
"bool": true,
}
m["root"] = interface{}(v)
xmlStr, err := gxml.EncodeWithIndent(m, "xml")
if err != nil {
t.Errorf("encodeWithIndent error.")
}
t.Logf("%s\n", string(xmlStr))
}

View File

@ -4,10 +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 gins provides instances management and some core components.
//
// 单例对象管理.
// 框架内置了一些核心对象获取方法并且可以通过Set和Get方法实现IoC以及对内置核心对象的自定义替换
// Package gins provides instances management and core components management.
package gins
import (
@ -21,9 +18,9 @@ import (
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gview"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"time"
)
@ -75,11 +72,18 @@ func View(name...string) *gview.View {
}
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_VIEW, group)
return instances.GetOrSetFuncLock(key, func() interface{} {
path := cmdenv.Get("gf.gview.path", gfile.Pwd()).String()
view := gview.New(path)
// 添加基于源码的搜索目录检索地址,常用于开发环境调试,只添加入口文件目录
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
view.AddPath(p)
view := gview.New(gfile.Pwd())
// 自定义的环境变量/启动参数路径,优先级最高,覆盖默认的工作目录
if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" && gfile.Exists(envPath) {
view.SetPath(envPath)
}
// 二进制文件执行目录
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
view.AddPath(selfPath)
}
// 开发环境源码main包目录
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
view.AddPath(mainPath)
}
// 框架内置函数
view.BindFunc("config", funcConfig)
@ -94,26 +98,9 @@ func Config(file...string) *gcfg.Config {
if len(file) > 0 {
configFile = file[0]
}
return instances.GetOrSetFuncLock(fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile),
func() interface{} {
pwdPath := gfile.Pwd()
envPath := cmdenv.Get("gf.gcfg.path").String()
selfPath := gfile.SelfDir()
mainPath := gfile.MainPkgPath()
config := gcfg.New(pwdPath, configFile)
// 自定义的环境变量/启动参数路径,优先级最高,覆盖默认的工作目录
if envPath != "" && gfile.Exists(envPath) {
config.SetPath(envPath)
}
// 二进制文件执行目录
if selfPath != "" && gfile.Exists(selfPath) {
config.AddPath(selfPath)
}
// 开发环境源码main包目录
if mainPath != "" && gfile.Exists(mainPath) {
config.AddPath(mainPath)
}
return config
key := fmt.Sprintf("%s.%s", gFRAME_CORE_COMPONENT_NAME_CONFIG, configFile)
return instances.GetOrSetFuncLock(key, func() interface{} {
return gcfg.New(configFile)
}).(*gcfg.Config)
}
@ -135,62 +122,66 @@ func Database(name...string) gdb.DB {
for group, v := range m {
cg := gdb.ConfigGroup{}
if list, ok := v.([]interface{}); ok {
for _, nodev := range list {
node := gdb.ConfigNode{}
nodem := nodev.(map[string]interface{})
if value, ok := nodem["host"]; ok {
for _, nodeValue := range list {
node := gdb.ConfigNode{}
nodeMap := nodeValue.(map[string]interface{})
if value, ok := nodeMap["host"]; ok {
node.Host = gconv.String(value)
}
if value, ok := nodem["port"]; ok {
if value, ok := nodeMap["port"]; ok {
node.Port = gconv.String(value)
}
if value, ok := nodem["user"]; ok {
if value, ok := nodeMap["user"]; ok {
node.User = gconv.String(value)
}
if value, ok := nodem["pass"]; ok {
if value, ok := nodeMap["pass"]; ok {
node.Pass = gconv.String(value)
}
if value, ok := nodem["name"]; ok {
if value, ok := nodeMap["name"]; ok {
node.Name = gconv.String(value)
}
if value, ok := nodem["type"]; ok {
if value, ok := nodeMap["type"]; ok {
node.Type = gconv.String(value)
}
if value, ok := nodem["role"]; ok {
if value, ok := nodeMap["role"]; ok {
node.Role = gconv.String(value)
}
if value, ok := nodem["charset"]; ok {
if value, ok := nodeMap["charset"]; ok {
node.Charset = gconv.String(value)
}
if value, ok := nodem["priority"]; ok {
if value, ok := nodeMap["priority"]; ok {
node.Priority = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["linkinfo"]; ok {
node.Linkinfo = gconv.String(value)
}
if value, ok := nodem["linkInfo"]; ok {
node.Linkinfo = gconv.String(value)
if value, ok := nodeMap["linkinfo"]; ok {
node.LinkInfo = gconv.String(value)
}
// Deprecated
if value, ok := nodem["max-idle"]; ok {
if value, ok := nodeMap["link-info"]; ok {
node.LinkInfo = gconv.String(value)
}
if value, ok := nodeMap["linkInfo"]; ok {
node.LinkInfo = gconv.String(value)
}
// Deprecated
if value, ok := nodeMap["max-idle"]; ok {
node.MaxIdleConnCount = gconv.Int(value)
}
if value, ok := nodem["maxIdle"]; ok {
if value, ok := nodeMap["maxIdle"]; ok {
node.MaxIdleConnCount = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["max-open"]; ok {
if value, ok := nodeMap["max-open"]; ok {
node.MaxOpenConnCount = gconv.Int(value)
}
if value, ok := nodem["maxOpen"]; ok {
if value, ok := nodeMap["maxOpen"]; ok {
node.MaxOpenConnCount = gconv.Int(value)
}
// Deprecated
if value, ok := nodem["max-lifetime"]; ok {
if value, ok := nodeMap["max-lifetime"]; ok {
node.MaxConnLifetime = gconv.Int(value)
}
if value, ok := nodem["maxLifetime"]; ok {
if value, ok := nodeMap["maxLifetime"]; ok {
node.MaxConnLifetime = gconv.Int(value)
}
cg = append(cg, node)
@ -198,10 +189,7 @@ func Database(name...string) gdb.DB {
}
gdb.AddConfigGroup(group, cg)
}
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空数据库配置缓存
gfsnotify.Add(config.GetFilePath(), func(event *gfsnotify.Event) {
instances.Remove(key)
})
addConfigMonitor(key, config)
}
if db, err := gdb.New(name...); err == nil {
return db
@ -231,29 +219,31 @@ func Redis(name...string) *gredis.Redis {
line := gconv.String(v)
array, _ := gregex.MatchString(`(.+):(\d+),{0,1}(\d*),{0,1}(.*)\?(.+)`, line)
if len(array) == 6 {
parse, _ := gstr.Parse(array[5])
config := gredis.Config{
parse, _ := gstr.Parse(array[5])
redisConfig := gredis.Config{
Host : array[1],
Port : gconv.Int(array[2]),
Db : gconv.Int(array[3]),
Pass : array[4],
}
if v, ok := parse["maxIdle"]; ok {
config.MaxIdle = gconv.Int(v)
redisConfig.MaxIdle = gconv.Int(v)
}
if v, ok := parse["maxActive"]; ok {
config.MaxActive = gconv.Int(v)
redisConfig.MaxActive = gconv.Int(v)
}
if v, ok := parse["idleTimeout"]; ok {
config.IdleTimeout = gconv.TimeDuration(v)*time.Second
redisConfig.IdleTimeout = gconv.TimeDuration(v)*time.Second
}
if v, ok := parse["maxConnLifetime"]; ok {
config.MaxConnLifetime = gconv.TimeDuration(v)*time.Second
redisConfig.MaxConnLifetime = gconv.TimeDuration(v)*time.Second
}
return gredis.New(config)
addConfigMonitor(key, config)
return gredis.New(redisConfig)
}
array, _ = gregex.MatchString(`(.+):(\d+),{0,1}(\d*),{0,1}(.*)`, line)
if len(array) == 5 {
addConfigMonitor(key, config)
return gredis.New(gredis.Config{
Host : array[1],
Port : gconv.Int(array[2]),
@ -277,6 +267,16 @@ func Redis(name...string) *gredis.Redis {
return nil
}
// 添加对单例对象的配置文件inotify监控
func addConfigMonitor(key string, config *gcfg.Config) {
// 使用gfsnotify进行文件监控当配置文件有任何变化时清空对象单例缓存
if path := config.GetFilePath(); path != "" {
gfsnotify.Add(path, func(event *gfsnotify.Event) {
instances.Remove(key)
})
}
}
// 模板内置方法config
func funcConfig(pattern string, file...string) string {
return Config().GetString(pattern, file...)

View File

@ -57,7 +57,7 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config().Get("test"), "v=1")
gtest.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0")
@ -71,7 +71,7 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config().Get("test"), "v=1")
gtest.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config().Get("redis.disk"), "127.0.0.1:6379,0")
@ -84,7 +84,7 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
@ -97,7 +97,7 @@ test = "v=1"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
gtest.Assert(gins.Config("test.toml").Get("database.default.1.host"), "127.0.0.1")
gtest.Assert(gins.Config("test.toml").Get("redis.disk"), "127.0.0.1:6379,0")
@ -113,7 +113,7 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config().AddPath(path), nil)
gtest.Assert(gins.Config().Get("test"), "v=1")
gtest.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1")
@ -127,7 +127,7 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config().Reload()
defer gins.Config().Clear()
gtest.Assert(gins.Config().AddPath(path), nil)
gtest.Assert(gins.Config().Get("test"), "v=1")
gtest.Assert(gins.Config().Get("database.default.1.host"), "127.0.0.1")
@ -141,7 +141,7 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config("test.toml").Reload()
defer gins.Config("test.toml").Clear()
gtest.Assert(gins.Config("test.toml").AddPath(path), nil)
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")
@ -156,7 +156,7 @@ test = "v=1"
err := gfile.PutContents(file, config)
gtest.Assert(err, nil)
defer gfile.Remove(file)
defer gins.Config("test.toml").Reload()
defer gins.Config("test.toml").Clear()
gtest.Assert(gins.Config("test.toml").AddPath(path), nil)
gtest.Assert(gins.Config("test.toml").Get("test"), "v=1")

View File

@ -53,7 +53,7 @@ test = "v=2"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
// for gfsnotify callbacks to refresh cache of config file
time.Sleep(500*time.Millisecond)

View File

@ -53,7 +53,7 @@ test = "v=3"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
defer gins.Config().Reload()
defer gins.Config().Clear()
// for gfsnotify callbacks to refresh cache of config file
time.Sleep(500*time.Millisecond)

View File

@ -56,7 +56,7 @@ func (r *Response) CORS(options CORSOptions) {
}
}
// 允许请求跨域访问(使用more配置).
// 允许请求跨域访问(使用默认配置).
func (r *Response) CORSDefault() {
r.CORS(r.DefaultCORSOptions())
}

View File

@ -15,7 +15,6 @@ import (
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/genv"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gproc"
"github.com/gogf/gf/g/os/gtimer"
@ -108,7 +107,10 @@ const (
HOOK_AFTER_SERVE = "AfterServe"
HOOK_BEFORE_OUTPUT = "BeforeOutput"
HOOK_AFTER_OUTPUT = "AfterOutput"
// deprecated.
HOOK_BEFORE_CLOSE = "BeforeClose"
// deprecated.
HOOK_AFTER_CLOSE = "AfterClose"
HTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
@ -187,9 +189,9 @@ func serverProcessInit() {
}
// 是否处于开发环境
if gfile.MainPkgPath() != "" {
glog.Debug("GF notices that you're in develop environment, so error logs are auto enabled to stdout.")
}
//if gfile.MainPkgPath() != "" {
// glog.Debug("GF notices that you're in develop environment, so error logs are auto enabled to stdout.")
//}
}
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
@ -217,8 +219,6 @@ func GetServer(name...interface{}) (*Server) {
servedCount : gtype.NewInt(),
logger : glog.New(),
}
// 日志的标准输出默认关闭,但是错误信息会特殊处理
s.logger.SetStdPrint(false)
// 初始化时使用默认配置
s.SetConfig(defaultServerConfig)
// 记录到全局ServerMap中
@ -237,6 +237,11 @@ func (s *Server) Start() error {
return errors.New("server is already running")
}
// 没有注册任何路由,且没有开启文件服务,那么提示错误
if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
glog.Fatal("[ghttp] no router set or static feature enabled, did you forget import the router?")
}
// 底层http server配置
if s.config.Handler == nil {
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
@ -277,7 +282,7 @@ func (s *Server) Start() error {
if gproc.IsChild() {
gtimer.SetTimeout(2*time.Second, func() {
if err := gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP); err != nil {
glog.Error("ghttp server error in process communication:", err)
glog.Error("[ghttp] server error in process communication:", err)
}
})
}

View File

@ -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) {

View File

@ -74,10 +74,11 @@ type ServerConfig struct {
Rewrites map[string]string // URI Rewrite重写配置
// 日志配置
LogPath string // 存放日志的目录路径
LogHandler LogHandler // 自定义日志处理回调方法
ErrorLogEnabled bool // 是否开启error log
AccessLogEnabled bool // 是否开启access log
LogPath string // 存放日志的目录路径(默认为空,表示不写文件)
LogHandler LogHandler // 自定义日志处理回调方法(默认为空)
LogStdPrint bool // 是否打印日志到终端(默认开启)
ErrorLogEnabled bool // 是否开启error log(默认开启)
AccessLogEnabled bool // 是否开启access log(默认关闭)
// 其他设置
NameToUriType int // 服务注册时对象和方法名称转换为URI时的规则
@ -110,12 +111,11 @@ var defaultServerConfig = ServerConfig {
SessionMaxAge : gDEFAULT_SESSION_MAX_AGE,
SessionIdName : gDEFAULT_SESSION_ID_NAME,
LogStdPrint : true,
ErrorLogEnabled : true,
AccessLogEnabled : false,
GzipContentTypes : defaultGzipContentTypes,
DumpRouteMap : true,
RouterCacheExpire : 60,
Rewrites : make(map[string]string),
}

View File

@ -10,7 +10,10 @@ import (
"github.com/gogf/gf/g/os/glog"
)
// 设置日志目录
// 设置日志目录,只有在设置了日志目录的情况下才会输出日志到日志文件中。
// 日志文件路径格式为:
// 1. 请求日志: access/YYYY-MM-DD.log
// 2. 错误日志: error/YYYY-MM-DD.log
func (s *Server)SetLogPath(path string) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
@ -23,6 +26,16 @@ func (s *Server)SetLogPath(path string) {
s.logger.SetPath(path)
}
// 设置日志内容是否输出到终端,默认情况下只有错误日志才会自动输出到终端。
// 如果需要输出请求日志到终端默认情况下使用SetAccessLogEnabled方法开启请求日志特性即可。
func (s *Server)SetLogStdPrint(enabled bool) {
if s.Status() == SERVER_STATUS_RUNNING {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
s.config.LogStdPrint = enabled
}
// 设置是否开启access log日志功能
func (s *Server)SetAccessLogEnabled(enabled bool) {
if s.Status() == SERVER_STATUS_RUNNING {
@ -51,7 +64,7 @@ func (s *Server) SetLogHandler(handler LogHandler) {
}
// 获取日志写入的回调函数
func (s *Server) GetLogHandler() LogHandler {
func (s *Server)GetLogHandler() LogHandler {
return s.config.LogHandler
}

View File

@ -57,15 +57,12 @@ func (s *Server)SetServerRoot(root string) {
return
}
// RealPath的作用除了校验地址正确性以外还转换分隔符号为当前系统正确的文件分隔符号
path := gfile.RealPath(root)
if path == "" {
path = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + root)
realPath, err := gfile.Search(root)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: %s`, err.Error()))
}
if path == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: path "%s" does not exist`, root))
}
glog.Debug("[ghttp] SetServerRoot path:", path)
s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)}
glog.Debug("[ghttp] SetServerRoot path:", realPath)
s.config.SearchPaths = []string{strings.TrimRight(realPath, gfile.Separator)}
s.config.FileServerEnabled = true
}
@ -75,13 +72,9 @@ func (s *Server) AddSearchPath(path string) {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
// RealPath的作用除了校验地址正确性以外,还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
}
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: path "%s" does not exist`, path))
realPath, err := gfile.Search(path)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: %s`, err.Error()))
}
s.config.SearchPaths = append(s.config.SearchPaths, realPath)
s.config.FileServerEnabled = true
@ -93,13 +86,9 @@ func (s *Server) AddStaticPath(prefix string, path string) {
glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR)
return
}
// RealPath的作用除了校验地址正确性以外,还转换分隔符号为当前系统正确的文件分隔符号
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
}
if realPath == "" {
glog.Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: path "%s" does not exist`, path))
realPath, err := gfile.Search(path)
if err != nil {
glog.Fatal(fmt.Sprintf(`[ghttp] AddStaticPath failed: %s`, err.Error()))
}
addItem := staticPathItem {
prefix : prefix,

View File

@ -35,6 +35,12 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
r.URL.Path = rewrite
}
}
// URI默认值
if r.URL.Path == "" {
r.URL.Path = "/"
}
// 去掉末尾的"/"号
if r.URL.Path != "/" {
for r.URL.Path[len(r.URL.Path) - 1] == '/' {
@ -52,6 +58,13 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_BEFORE_OUTPUT, request)
}
// error log
if e := recover(); e != nil {
request.Response.WriteStatus(http.StatusInternalServerError)
s.handleErrorLog(e, request)
}
// access log
s.handleAccessLog(request)
// 输出Cookie
request.Cookie.Output()
// 输出缓冲区
@ -60,18 +73,8 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) {
if !request.IsExited() {
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
}
// 事件 - BeforeClose
s.callHookHandler(HOOK_BEFORE_CLOSE, request)
// access log
s.handleAccessLog(request)
// error log使用recover进行判断
if e := recover(); e != nil {
request.Response.WriteStatus(http.StatusInternalServerError)
s.handleErrorLog(e, request)
}
// 更新Session会话超时时间
request.Session.UpdateExpire()
s.callHookHandler(HOOK_AFTER_CLOSE, request)
}()
// ============================================================

View File

@ -9,7 +9,6 @@ package ghttp
import (
"fmt"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gtime"
)
@ -23,19 +22,19 @@ func (s *Server) handleAccessLog(r *Request) {
v(r)
return
}
content := fmt.Sprintf(`"%s %s %s %s" %d`,
r.Method, r.Host, r.URL.String(), r.Proto,
content := fmt.Sprintf(`%d "%s %s %s %s"`,
r.Response.Status,
r.Method, r.Host, r.URL.String(), r.Proto,
)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("access").Backtrace(false, 2).Println(content)
s.logger.Cat("access").Backtrace(false, 2).StdPrint(s.config.LogStdPrint).Println(content)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
func (s *Server) handleErrorLog(error interface{}, r *Request) {
// 错误输出默认是开启的
if !s.IsErrorLogEnabled() && gfile.MainPkgPath() == "" {
if !s.IsErrorLogEnabled() {
return
}
@ -53,15 +52,5 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) {
content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond() - r.EnterTime)/1000)
}
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
if s.logger.GetPath() == "" {
// 错误信息特殊处理,在未开启日志文件保存时强制强制输出到终端
s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content)
} else {
s.logger.Cat("error").Backtrace(true, 2).Error(content)
// 开发环境下(MainPkgPath)自动输出错误信息到标准输出
if gfile.MainPkgPath() != "" {
s.logger.Cat("error").Backtrace(true, 2).StdPrint(true).Error(content)
}
}
s.logger.Cat("error").Backtrace(true, 2).StdPrint(s.config.LogStdPrint).Error(content)
}

View File

@ -43,14 +43,22 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) {
if mname == "Init" || mname == "Shut" || mname == "Exit" {
continue
}
if _, ok := v.Method(i).Interface().(func()); !ok {
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
continue
}
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if ctlName[0] == '*' {
ctlName = fmt.Sprintf(`(%s)`, ctlName)
}
if _, ok := v.Method(i).Interface().(func()); !ok {
if len(methodMap) > 0 {
// 指定的方法名称注册,那么需要使用错误提示
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
} else {
// 否则只是Debug提示
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
}
continue
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem {
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
@ -93,16 +101,17 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
glog.Error("invalid method name:" + mname)
return
}
if _, ok := fval.Interface().(func()); !ok {
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, fval.Type().String())
return
}
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if ctlName[0] == '*' {
ctlName = fmt.Sprintf(`(%s)`, ctlName)
}
if _, ok := fval.Interface().(func()); !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
pkgPath, ctlName, mname, fval.Type().String())
return
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem {
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),
@ -132,15 +141,16 @@ func (s *Server)BindControllerRest(pattern string, c Controller) {
if _, ok := methodsMap[method]; !ok {
continue
}
if _, ok := v.Method(i).Interface().(func()); !ok {
glog.Errorfln(`invalid method definition "%s", while "func()" is required`, v.Method(i).Type().String())
return
}
pkgName := gfile.Basename(pkgPath)
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if ctlName[0] == '*' {
ctlName = fmt.Sprintf(`(%s)`, ctlName)
}
if _, ok := v.Method(i).Interface().(func()); !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required`,
pkgPath, ctlName, mname, v.Method(i).Type().String())
return
}
key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false)
m[key] = &handlerItem {
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname),

View File

@ -49,15 +49,23 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) {
if mname == "Init" || mname == "Shut" {
continue
}
faddr, ok := v.Method(i).Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid method definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String())
continue
}
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := v.Method(i).Interface().(func(*Request))
if !ok {
if len(methodMap) > 0 {
// 指定的方法名称注册,那么需要使用错误提示
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
pkgPath, objName, mname, v.Method(i).Type().String())
} else {
// 否则只是Debug提示
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
pkgPath, objName, mname, v.Method(i).Type().String())
}
continue
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, true)
m[key] = &handlerItem {
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
@ -103,11 +111,6 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
glog.Error("invalid method name:" + mname)
return
}
faddr, ok := fval.Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, fval.Type().String())
return
}
finit := (func(*Request))(nil)
fshut := (func(*Request))(nil)
if v.MethodByName("Init").IsValid() {
@ -122,6 +125,12 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := fval.Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
pkgPath, objName, mname, fval.Type().String())
return
}
key := s.mergeBuildInNameToPattern(pattern, sname, mname, false)
m[key] = &handlerItem{
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),
@ -158,16 +167,17 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) {
if _, ok := methodsMap[method]; !ok {
continue
}
faddr, ok := v.Method(i).Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid method definition "%s", while "func(*Request)" is required`, v.Method(i).Type().String())
continue
}
pkgName := gfile.Basename(pkgPath)
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
}
faddr, ok := v.Method(i).Interface().(func(*Request))
if !ok {
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required`,
pkgPath, objName, mname, v.Method(i).Type().String())
continue
}
key := s.mergeBuildInNameToPattern(mname + ":" + pattern, sname, mname, false)
m[key] = &handlerItem {
name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname),

View File

@ -30,7 +30,7 @@ var serverMapping = gmap.NewStringInterfaceMap()
// 获取/创建一个空配置的TCP Server
// 单例模式请保证name的唯一性
func GetServer(name...interface{}) (*Server) {
func GetServer(name...interface{}) *Server {
serverName := gDEFAULT_SERVER
if len(name) > 0 {
serverName = gconv.String(name[0])

View File

@ -221,8 +221,11 @@ func (c *Conn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
// 不能使用c.conn.RemoteAddr()其返回为nil
// 这里使用c.raddr获取远程连接地址。
func (c *Conn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
//return c.conn.RemoteAddr()
return c.raddr
}
func (c *Conn) Close() error {

View File

@ -57,7 +57,7 @@ func TestCache_LRU(t *testing.T) {
gtest.Assert(cache.Size(), 10)
gtest.Assert(cache.Get(6), 6)
time.Sleep(3*time.Second)
time.Sleep(4*time.Second)
gtest.Assert(cache.Size(), 2)
gtest.Assert(cache.Get(6), 6)
gtest.Assert(cache.Get(1), nil)

View File

@ -4,44 +4,41 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gcfg provides reading, caching and managing for configuration files.
//
// 配置管理,
// 配置文件格式支持json, xml, toml, yaml/yml
// Package gcfg provides reading, caching and managing for configuration files/contents.
package gcfg
import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gspath"
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/internal/cmdenv"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfsnotify"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gspath"
)
const (
// 默认的配置管理文件名称
// Default configuration file name.
DEFAULT_CONFIG_FILE = "config.toml"
)
// 配置管理对象
// Configuration struct.
type Config struct {
name *gtype.String // 默认配置文件名称
paths *garray.StringArray // 搜索目录路径
jsons *gmap.StringInterfaceMap // 配置文件对象
vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false检测会比较影响检索效率)
name *gtype.String // Default configuration file name.
paths *garray.StringArray // Searching path array.
jsons *gmap.StringInterfaceMap // The pared JSON objects for configuration files.
vc *gtype.Bool // Whether do violence check in value index searching.
// It affects the performance when set true(false in default).
}
// New returns a new configuration management object.
//
// 生成一个配置管理对象
func New(path string, file...string) *Config {
func New(file...string) *Config {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
@ -52,15 +49,24 @@ func New(path string, file...string) *Config {
jsons : gmap.NewStringInterfaceMap(),
vc : gtype.NewBool(),
}
if len(path) > 0 {
c.SetPath(path)
}
// Dir path of working dir.
c.SetPath(gfile.Pwd())
// Dir path from env/cmd, most high priority, will overwrite previous dir path setting.
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" && gfile.Exists(envPath) {
c.SetPath(envPath)
}
// Dir path of binary.
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
c.AddPath(selfPath)
}
// Dir path of main package.
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
c.AddPath(mainPath)
}
return c
}
// filePath returns the absolute configuration file path for the given filename by <file>.
//
// 判断从哪个配置文件中获取内容,返回配置文件的绝对路径
func (c *Config) filePath(file...string) (path string) {
name := c.name.Val()
if len(file) > 0 {
@ -86,10 +92,40 @@ func (c *Config) filePath(file...string) (path string) {
// 设置配置管理器的配置文件存放目录绝对路径
func (c *Config) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gcfg] SetPath failed: %s`, err.Error()))
// 判断相对路径
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] SetPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -107,15 +143,45 @@ func (c *Config) SetPath(path string) error {
// 开启比较耗性能,也不建议允许键名中存在分隔符,最好在应用端避免这种情况。
func (c *Config) SetViolenceCheck(check bool) {
c.vc.Set(check)
c.Reload()
c.Clear()
}
// 添加配置管理器的配置文件搜索路径
func (c *Config) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gcfg] AddPath failed: %s`, err.Error()))
// 判断相对路径
c.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
if c.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gcfg] AddPath failed: cannot find directory \"%s\" in following paths:", path))
c.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gcfg] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -168,19 +234,30 @@ func (c *Config) getJson(file...string) *gjson.Json {
name = file[0]
}
r := c.jsons.GetOrSetFuncLock(name, func() interface{} {
filePath := c.filePath(file...)
if filePath == "" {
return nil
content := ""
filePath := ""
if content = GetContent(name); content == "" {
filePath = c.filePath(name)
if filePath == "" {
return nil
}
content = gfile.GetContents(filePath)
}
if j, err := gjson.Load(filePath); err == nil {
if j, err := gjson.LoadContent(content); err == nil {
j.SetViolenceCheck(c.vc.Val())
// 添加配置文件监听,如果有任何变化,删除文件内容缓存,下一次查询会自动更新
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
})
if filePath != "" {
gfsnotify.Add(filePath, func(event *gfsnotify.Event) {
c.jsons.Remove(name)
})
}
return j
} else {
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
if filePath != "" {
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
} else {
glog.Criticalfln(`[gcfg] Load configuration failed: %s`, err.Error())
}
}
return nil
})
@ -372,8 +449,14 @@ func (c *Config) GetToStruct(pattern string, objPointer interface{}, file...stri
return errors.New("config file not found")
}
// 清空当前配置文件缓存,强制重新从磁盘文件读取配置文件内容
// Deprecated. See Clear.
func (c *Config) Reload() {
c.jsons.Clear()
}
// Clear removes all parsed configuration files content cache,
// which will force reload configuration content from file.
func (c *Config) Clear() {
c.jsons.Clear()
}

39
g/os/gcfg/gcfg_config.go Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2019 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 gcfg
import "github.com/gogf/gf/g/container/gmap"
var (
// Customized configuration content.
configs = gmap.NewStringStringMap()
)
// SetContent sets customized configuration content for specified <file>.
// The <file> is unnecessary param, default is DEFAULT_CONFIG_FILE.
func SetContent(content string, file...string) {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
}
configs.Set(name, content)
}
// GetContent returns customized configuration content for specified <file>.
// The <file> is unnecessary param, default is DEFAULT_CONFIG_FILE.
func GetContent(file...string) string {
name := DEFAULT_CONFIG_FILE
if len(file) > 0 {
name = file[0]
}
return configs.Get(name)
}
// ClearContent removes all global configuration contents.
func ClearContent() {
configs.Clear()
}

View File

@ -0,0 +1,27 @@
// Copyright 2019 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 gcfg
import (
"github.com/gogf/gf/g/container/gmap"
)
var (
// Instances map.
instances = gmap.NewStringInterfaceMap()
)
// Instance returns an instance of Config.
func Instance(file...string) *Config {
configFile := DEFAULT_CONFIG_FILE
if len(file) > 0 {
configFile = file[0]
}
return instances.GetOrSetFuncLock(configFile, func() interface{} {
return New(configFile)
}).(*Config)
}

View File

@ -27,12 +27,12 @@ array = [1,2,3]
cache = "127.0.0.1:6379,1"
`
gtest.Case(t, func() {
path := "config.toml"
path := gcfg.DEFAULT_CONFIG_FILE
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
c := gcfg.New(".")
c := gcfg.New()
gtest.Assert(c.Get("v1"), 1)
gtest.AssertEQ(c.GetInt("v1"), 1)
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
@ -71,7 +71,201 @@ array = [1,2,3]
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1",
})
gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + "config.toml")
gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path)
})
}
func Test_Content(t *testing.T) {
content := `
v1 = 1
v2 = "true"
v3 = "off"
v4 = "1.23"
array = [1,2,3]
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
`
gcfg.SetContent(content)
defer gcfg.ClearContent()
gtest.Case(t, func() {
c := gcfg.New()
gtest.Assert(c.Get("v1"), 1)
gtest.AssertEQ(c.GetInt("v1"), 1)
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
gtest.AssertEQ(c.GetInt16("v1"), int16(1))
gtest.AssertEQ(c.GetInt32("v1"), int32(1))
gtest.AssertEQ(c.GetInt64("v1"), int64(1))
gtest.AssertEQ(c.GetUint("v1"), uint(1))
gtest.AssertEQ(c.GetUint8("v1"), uint8(1))
gtest.AssertEQ(c.GetUint16("v1"), uint16(1))
gtest.AssertEQ(c.GetUint32("v1"), uint32(1))
gtest.AssertEQ(c.GetUint64("v1"), uint64(1))
gtest.AssertEQ(c.GetVar("v1").String(), "1")
gtest.AssertEQ(c.GetVar("v1").Bool(), true)
gtest.AssertEQ(c.GetVar("v2").String(), "true")
gtest.AssertEQ(c.GetVar("v2").Bool(), true)
gtest.AssertEQ(c.GetString("v1"), "1")
gtest.AssertEQ(c.GetFloat32("v4"), float32(1.23))
gtest.AssertEQ(c.GetFloat64("v4"), float64(1.23))
gtest.AssertEQ(c.GetString("v2"), "true")
gtest.AssertEQ(c.GetBool("v2"), true)
gtest.AssertEQ(c.GetBool("v3"), false)
gtest.AssertEQ(c.Contains("v1"), true)
gtest.AssertEQ(c.Contains("v2"), true)
gtest.AssertEQ(c.Contains("v3"), true)
gtest.AssertEQ(c.Contains("v4"), true)
gtest.AssertEQ(c.Contains("v5"), false)
gtest.AssertEQ(c.GetInts("array"), []int{1,2,3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1",
})
})
}
func Test_SetFileName(t *testing.T) {
config := `
{
"array": [
1,
2,
3
],
"redis": {
"cache": "127.0.0.1:6379,1",
"disk": "127.0.0.1:6379,0"
},
"v1": 1,
"v2": "true",
"v3": "off",
"v4": "1.234"
}
`
gtest.Case(t, func() {
path := "config.json"
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
c := gcfg.New()
c.SetFileName(path)
gtest.Assert(c.Get("v1"), 1)
gtest.AssertEQ(c.GetInt("v1"), 1)
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
gtest.AssertEQ(c.GetInt16("v1"), int16(1))
gtest.AssertEQ(c.GetInt32("v1"), int32(1))
gtest.AssertEQ(c.GetInt64("v1"), int64(1))
gtest.AssertEQ(c.GetUint("v1"), uint(1))
gtest.AssertEQ(c.GetUint8("v1"), uint8(1))
gtest.AssertEQ(c.GetUint16("v1"), uint16(1))
gtest.AssertEQ(c.GetUint32("v1"), uint32(1))
gtest.AssertEQ(c.GetUint64("v1"), uint64(1))
gtest.AssertEQ(c.GetVar("v1").String(), "1")
gtest.AssertEQ(c.GetVar("v1").Bool(), true)
gtest.AssertEQ(c.GetVar("v2").String(), "true")
gtest.AssertEQ(c.GetVar("v2").Bool(), true)
gtest.AssertEQ(c.GetString("v1"), "1")
gtest.AssertEQ(c.GetFloat32("v4"), float32(1.234))
gtest.AssertEQ(c.GetFloat64("v4"), float64(1.234))
gtest.AssertEQ(c.GetString("v2"), "true")
gtest.AssertEQ(c.GetBool("v2"), true)
gtest.AssertEQ(c.GetBool("v3"), false)
gtest.AssertEQ(c.Contains("v1"), true)
gtest.AssertEQ(c.Contains("v2"), true)
gtest.AssertEQ(c.Contains("v3"), true)
gtest.AssertEQ(c.Contains("v4"), true)
gtest.AssertEQ(c.Contains("v5"), false)
gtest.AssertEQ(c.GetInts("array"), []int{1,2,3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1",
})
gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path)
})
}
func Test_Instance(t *testing.T) {
config := `
{
"array": [
1,
2,
3
],
"redis": {
"cache": "127.0.0.1:6379,1",
"disk": "127.0.0.1:6379,0"
},
"v1": 1,
"v2": "true",
"v3": "off",
"v4": "1.234"
}
`
gtest.Case(t, func() {
path := gcfg.DEFAULT_CONFIG_FILE
err := gfile.PutContents(path, config)
gtest.Assert(err, nil)
defer gfile.Remove(path)
c := gcfg.Instance()
gtest.Assert(c.Get("v1"), 1)
gtest.AssertEQ(c.GetInt("v1"), 1)
gtest.AssertEQ(c.GetInt8("v1"), int8(1))
gtest.AssertEQ(c.GetInt16("v1"), int16(1))
gtest.AssertEQ(c.GetInt32("v1"), int32(1))
gtest.AssertEQ(c.GetInt64("v1"), int64(1))
gtest.AssertEQ(c.GetUint("v1"), uint(1))
gtest.AssertEQ(c.GetUint8("v1"), uint8(1))
gtest.AssertEQ(c.GetUint16("v1"), uint16(1))
gtest.AssertEQ(c.GetUint32("v1"), uint32(1))
gtest.AssertEQ(c.GetUint64("v1"), uint64(1))
gtest.AssertEQ(c.GetVar("v1").String(), "1")
gtest.AssertEQ(c.GetVar("v1").Bool(), true)
gtest.AssertEQ(c.GetVar("v2").String(), "true")
gtest.AssertEQ(c.GetVar("v2").Bool(), true)
gtest.AssertEQ(c.GetString("v1"), "1")
gtest.AssertEQ(c.GetFloat32("v4"), float32(1.234))
gtest.AssertEQ(c.GetFloat64("v4"), float64(1.234))
gtest.AssertEQ(c.GetString("v2"), "true")
gtest.AssertEQ(c.GetBool("v2"), true)
gtest.AssertEQ(c.GetBool("v3"), false)
gtest.AssertEQ(c.Contains("v1"), true)
gtest.AssertEQ(c.Contains("v2"), true)
gtest.AssertEQ(c.Contains("v3"), true)
gtest.AssertEQ(c.Contains("v4"), true)
gtest.AssertEQ(c.Contains("v5"), false)
gtest.AssertEQ(c.GetInts("array"), []int{1,2,3})
gtest.AssertEQ(c.GetStrings("array"), []string{"1","2","3"})
gtest.AssertEQ(c.GetArray("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetInterfaces("array"), []interface{}{"1","2","3"})
gtest.AssertEQ(c.GetMap("redis"), map[string]interface{}{
"disk" : "127.0.0.1:6379,0",
"cache" : "127.0.0.1:6379,1",
})
gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path)
})
}

View File

@ -141,11 +141,6 @@ func (c *Cron) Search(name string) *Entry {
return nil
}
// 根据指定名称删除定时任务
func (c *Cron) Remove(name string) {
c.entries.Remove(name)
}
// 开启定时任务执行(可以指定特定名称的一个或若干个定时任务)
func (c *Cron) Start(name...string) {
if len(name) > 0 {
@ -172,6 +167,13 @@ func (c *Cron) Stop(name...string) {
}
}
// 根据指定名称删除定时任务。
func (c *Cron) Remove(name string) {
if v := c.entries.Get(name); v != nil {
v.(*Entry).Close()
}
}
// 关闭定时任务
func (c *Cron) Close() {
c.status.Set(STATUS_CLOSED)

View File

@ -87,7 +87,7 @@ func (entry *Entry) Stop() {
// 关闭定时任务
func (entry *Entry) Close() {
entry.cron.Remove(entry.Name)
entry.cron.entries.Remove(entry.Name)
entry.entry.Close()
}
@ -96,6 +96,7 @@ func (entry *Entry) check() {
if entry.schedule.meet(time.Now()) {
path := entry.cron.GetLogPath()
level := entry.cron.GetLogLevel()
// 检查定时任务对象状态(非任务状态)
switch entry.cron.status.Val() {
case STATUS_STOPPED:
return

View File

@ -71,6 +71,24 @@ func TestCron_Basic(t *testing.T) {
})
}
func TestCron_Remove(t *testing.T) {
gtest.Case(t, func() {
cron := gcron.New()
array := garray.New()
cron.Add("* * * * * *", func() {
array.Append(1)
}, "add")
gtest.Assert(array.Len(), 0)
time.Sleep(1200*time.Millisecond)
gtest.Assert(array.Len(), 1)
cron.Remove("add")
gtest.Assert(array.Len(), 1)
time.Sleep(1200*time.Millisecond)
gtest.Assert(array.Len(), 1)
})
}
func TestCron_AddSingleton(t *testing.T) {
// un used, can be removed
gtest.Case(t, func() {

View File

@ -0,0 +1,63 @@
// Copyright 2017-2018 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 gfile
import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/garray"
)
// 如果给定绝对路径将会去掉其中的相对路径符号后返回;
// 如果是给定的相对路径,那么将会按照以下路径优先级搜索文件(重复路径会去重)
// prioritySearchPaths、当前工作目录、二进制文件目录、源码main包目录(开发环境下)
func Search(name string, prioritySearchPaths...string) (realPath string, err error) {
// 是否绝对路径
realPath = RealPath(name)
if realPath != "" {
return
}
// 相对路径搜索
array := garray.NewStringArray(true)
// 自定义优先路径
array.Append(prioritySearchPaths...)
// 用户工作目录
array.Append(Pwd())
// 二进制所在目录
array.Append(SelfDir())
// 源码main包目录
if path := MainPkgPath(); path != "" {
array.Append(path)
}
// 路径去重
array.Unique()
// 执行相对路径搜索
array.RLockFunc(func(array []string) {
path := ""
for _, v := range array {
path = RealPath(v + Separator + name)
if path != "" {
realPath = path
break
}
}
})
// 目录不存在错误处理
if realPath == "" {
buffer := bytes.NewBuffer(nil)
buffer.WriteString(fmt.Sprintf("cannot find file/folder \"%s\" in following paths:", name))
array.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s", k + 1, v))
}
})
err = errors.New(buffer.String())
}
return
}

View File

@ -24,8 +24,8 @@ import (
"github.com/gogf/gf/g/os/gspath"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/os/gview/internal/text/template"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/util/gconv"
"strings"
"sync"
)
@ -48,16 +48,10 @@ type FuncMap = map[string]interface{}
// 默认的视图对象
var viewObj *View
// 初始化默认的视图对象
// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。
func checkAndInitDefaultView() {
if viewObj == nil {
// gfile.MainPkgPath() 用以判断是否开发环境
mainPkgPath := gfile.MainPkgPath()
if gfile.MainPkgPath() == "" {
viewObj = New(gfile.Pwd())
} else {
viewObj = New(mainPkgPath)
}
viewObj = New(gfile.Pwd())
}
}
@ -106,13 +100,40 @@ func New(path...string) *View {
// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
// 判断相对路径
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] SetPath failed: %s`, err.Error()))
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gview] SetPath failed: cannot find directory \"%s\" in following paths:", path))
view.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断
@ -127,13 +148,40 @@ func (view *View) SetPath(path string) error {
// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
// 判断绝对路径(或者工作目录下目录)
realPath := gfile.RealPath(path)
if realPath == "" {
realPath = gfile.RealPath(gfile.MainPkgPath() + gfile.Separator + path)
// 判断相对路径
view.paths.RLockFunc(func(array []string) {
for _, v := range array {
if path, _ := gspath.Search(v, path); path != "" {
realPath = path
break
}
}
})
}
// 目录不存在错误处理
if realPath == "" {
err := errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
glog.Error(fmt.Sprintf(`[gview] AddPath failed: %s`, err.Error()))
buffer := bytes.NewBuffer(nil)
if view.paths.Len() > 0 {
buffer.WriteString(fmt.Sprintf("[gview] AddPath failed: cannot find directory \"%s\" in following paths:", path))
view.paths.RLockFunc(func(array []string) {
for k, v := range array {
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
}
})
} else {
buffer.WriteString(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path))
}
err := errors.New(buffer.String())
glog.Error(err)
return err
}
// 路径必须为目录类型
if !gfile.IsDir(realPath) {
err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
glog.Error(err)
return err
}
// 重复判断

View File

@ -3,15 +3,14 @@ package main
import "fmt"
func main() {
array := []uint{1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6}
for i := 0; i < len(array) - 1; i++ {
for j := i + 1; j < len(array); j++ {
if array[i] == array[j] {
array = append(array[ : j], array[j + 1 : ]...)
}
}
}
fmt.Println(array)
array := []uint{1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6}
for i := 0; i < len(array)-1; i++ {
for j := i + 1; j < len(array); j++ {
if array[i] == array[j] {
array = append(array[:j], array[j+1:]...)
}
}
}
fmt.Println(array)
}
}

View File

@ -1,59 +1,58 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/garray"
"fmt"
"github.com/gogf/gf/g/container/garray"
)
func main() {
// 创建普通的int类型数组并关闭默认的并发安全特性
a := garray.NewIntArray(true)
func main () {
// 创建普通的int类型数组并关闭默认的并发安全特性
a := garray.NewIntArray(true)
// 添加数据项
for i := 0; i < 10; i++ {
a.Append(i)
}
// 添加数据项
for i := 0; i < 10; i++ {
a.Append(i)
}
// 获取当前数组长度
fmt.Println(a.Len())
// 获取当前数组长度
fmt.Println(a.Len())
// 获取当前数据项列表
fmt.Println(a.Slice())
// 获取当前数据项列表
fmt.Println(a.Slice())
// 获取指定索引项
fmt.Println(a.Get(6))
// 获取指定索引项
fmt.Println(a.Get(6))
// 指定索引前插入数据
a.InsertAfter(9, 11)
// 在指定索引后插入数据项
a.InsertBefore(10, 10)
fmt.Println(a.Slice())
// 指定索引前插入数据项
a.InsertAfter(9, 11)
// 在指定索引后插入数据项
a.InsertBefore(10, 10)
fmt.Println(a.Slice())
// 修改指定索引数据项
a.Set(0, 100)
fmt.Println(a.Slice())
// 修改指定索引的数据项
a.Set(0, 100)
fmt.Println(a.Slice())
// 搜索数据项,返回搜索到的索引位置
fmt.Println(a.Search(5))
// 搜索数据项,返回搜索到的索引位置
fmt.Println(a.Search(5))
// 删除指定索引的数据项
a.Remove(0)
fmt.Println(a.Slice())
// 删除指定索引的数据项
a.Remove(0)
fmt.Println(a.Slice())
// 并发安全,写锁操作
a.LockFunc(func(array []int) {
// 将末尾项改为100
array[len(array)-1] = 100
})
// 并发安全,锁操作
a.LockFunc(func(array []int) {
// 将末尾项改为100
array[len(array) - 1] = 100
})
// 并发安全,锁操作
a.RLockFunc(func(array []int) {
fmt.Println(array[len(array)-1])
})
// 并发安全,读锁操作
a.RLockFunc(func(array []int) {
fmt.Println(array[len(array) - 1])
})
// 清空数组
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
// 清空数组
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
}

View File

@ -1,40 +1,39 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/garray"
"fmt"
"github.com/gogf/gf/g/container/garray"
)
func main() {
// 自定义排序数组,降序排序(SortedIntArray管理的数据是升序)
a := garray.NewSortedArray(func(v1, v2 interface{}) int {
if v1.(int) < v2.(int) {
return 1
}
if v1.(int) > v2.(int) {
return -1
}
return 0
})
func main () {
// 自定义排序数组,降序排序(SortedIntArray管理的数据是升序)
a := garray.NewSortedArray(func(v1, v2 interface{}) int {
if v1.(int) < v2.(int) {
return 1
}
if v1.(int) > v2.(int) {
return -1
}
return 0
})
// 添加数据
a.Add(2)
a.Add(3)
a.Add(1)
fmt.Println(a.Slice())
// 添加数据
a.Add(2)
a.Add(3)
a.Add(1)
fmt.Println(a.Slice())
// 添加重复数据
a.Add(3)
fmt.Println(a.Slice())
// 添加重复数据
a.Add(3)
fmt.Println(a.Slice())
// 检索数据,返回最后对比的索引位置,检索结果
// 检索结果0: 匹配; <0:参数小于对比值; >0:参数大于对比值
fmt.Println(a.Search(1))
// 检索数据,返回最后对比的索引位置,检索结果
// 检索结果0: 匹配; <0:参数小于对比值; >0:参数大于对比值
fmt.Println(a.Search(1))
// 设置不可重复
a.SetUnique(true)
fmt.Println(a.Slice())
a.Add(1)
fmt.Println(a.Slice())
// 设置不可重复
a.SetUnique(true)
fmt.Println(a.Slice())
a.Add(1)
fmt.Println(a.Slice())
}

View File

@ -1,18 +1,17 @@
package main
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
)
func main() {
a := garray.NewIntArray()
a.Append(1, 2, 3)
func main () {
a := garray.NewIntArray()
a.Append(1, 2, 3)
v := a.Slice()
v[0] = 4
v := a.Slice()
v[0] = 4
g.Dump(a.Slice())
g.Dump(v)
g.Dump(a.Slice())
g.Dump(v)
}

View File

@ -1,20 +1,20 @@
package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
)
func main() {
array := garray.NewSortedStringArray()
array.Add("1")
array.Add("2")
array.Add("3")
array.Add("4")
array.Add("5")
array.Add("6")
array.Add("7")
array.Add("8")
array.Add("9")
g.Dump(array.Slice())
}
func main() {
array := garray.NewSortedStringArray()
array.Add("1")
array.Add("2")
array.Add("3")
array.Add("4")
array.Add("5")
array.Add("6")
array.Add("7")
array.Add("8")
array.Add("9")
g.Dump(array.Slice())
}

View File

@ -1,26 +1,26 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
"strings"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
"strings"
)
func main() {
array := garray.NewSortedStringArray()
array.Add("/api/ctl/show")
array.Add("/api/ctl/post")
array.Add("/api/obj/rest")
array.Add("/api/handler")
array.Add("/api/obj/delete")
array.Add("/api/obj/show")
array.Add("/api/obj/my-show")
array.Add("/api/*")
array.Add("/api/ctl/rest")
array.Add("/api/ctl/my-show")
g.Dump(array.Slice())
func main() {
array := garray.NewSortedStringArray()
array.Add("/api/ctl/show")
array.Add("/api/ctl/post")
array.Add("/api/obj/rest")
array.Add("/api/handler")
array.Add("/api/obj/delete")
array.Add("/api/obj/show")
array.Add("/api/obj/my-show")
array.Add("/api/*")
array.Add("/api/ctl/rest")
array.Add("/api/ctl/my-show")
g.Dump(array.Slice())
fmt.Println(strings.Compare("/api/ctl/post", "/api/*"))
fmt.Println(strings.Compare("/api/*", "/api/ctl/my-show"))
}
fmt.Println(strings.Compare("/api/ctl/post", "/api/*"))
fmt.Println(strings.Compare("/api/*", "/api/ctl/my-show"))
}

View File

@ -1,73 +1,73 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/gmap"
"fmt"
"github.com/gogf/gf/g/container/gmap"
)
func main() {
// 创建一个默认的gmap对象
// 默认情况下该gmap对象支持并发安全特性
// 初始化时可以给定false参数关闭并发安全特性当做一个普通的map使用。
m := gmap.New()
// 创建一个默认的gmap对象
// 默认情况下该gmap对象支持并发安全特性
// 初始化时可以给定false参数关闭并发安全特性当做一个普通的map使用。
m := gmap.New()
// 设置键值对
for i := 0; i < 10; i++ {
m.Set(i, i)
}
// 查询大小
fmt.Println(m.Size())
// 批量设置键值对(不同的数据类型对象参数不同)
m.BatchSet(map[interface{}]interface{}{
10 : 10,
11 : 11,
})
fmt.Println(m.Size())
// 设置键值对
for i := 0; i < 10; i++ {
m.Set(i, i)
}
// 查询大小
fmt.Println(m.Size())
// 批量设置键值对(不同的数据类型对象参数不同)
m.BatchSet(map[interface{}]interface{}{
10: 10,
11: 11,
})
fmt.Println(m.Size())
// 查询是否存在
fmt.Println(m.Contains(1))
// 查询是否存在
fmt.Println(m.Contains(1))
// 查询键值
fmt.Println(m.Get(1))
// 查询键值
fmt.Println(m.Get(1))
// 删除数据项
m.Remove(9)
fmt.Println(m.Size())
// 删除数据项
m.Remove(9)
fmt.Println(m.Size())
// 批量删除
m.BatchRemove([]interface{}{10, 11})
fmt.Println(m.Size())
// 批量删除
m.BatchRemove([]interface{}{10, 11})
fmt.Println(m.Size())
// 当前键名列表(随机排序)
fmt.Println(m.Keys())
// 当前键值列表(随机排序)
fmt.Println(m.Values())
// 当前键名列表(随机排序)
fmt.Println(m.Keys())
// 当前键值列表(随机排序)
fmt.Println(m.Values())
// 查询键名,当键值不存在时,写入给定的默认值
fmt.Println(m.GetWithDefault(100, 100))
// 查询键名,当键值不存在时,写入给定的默认值
fmt.Println(m.GetWithDefault(100, 100))
// 删除键值对,并返回对应的键值
fmt.Println(m.GetAndRemove(100))
// 删除键值对,并返回对应的键值
fmt.Println(m.GetAndRemove(100))
// 遍历map
m.Iterator(func(k interface{}, v interface{}) bool {
fmt.Printf("%v:%v ", k, v)
return true
})
// 遍历map
m.Iterator(func(k interface{}, v interface{}) bool {
fmt.Printf("%v:%v ", k, v)
return true
})
// 自定义写锁操作
m.LockFunc(func(m map[interface{}]interface{}) {
m[99] = 99
})
// 自定义写锁操作
m.LockFunc(func(m map[interface{}]interface{}) {
m[99] = 99
})
// 自定义读锁操作
m.RLockFunc(func(m map[interface{}]interface{}) {
fmt.Println(m[99])
})
// 自定义读锁操作
m.RLockFunc(func(m map[interface{}]interface{}) {
fmt.Println(m[99])
})
// 清空map
m.Clear()
// 清空map
m.Clear()
// 判断map是否为空
fmt.Println(m.IsEmpty())
// 判断map是否为空
fmt.Println(m.IsEmpty())
}

View File

@ -1,17 +1,17 @@
package main
import (
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/gmap"
)
func main() {
m := gmap.New()
m.Set("1", "1")
m := gmap.New()
m.Set("1", "1")
m1 := m.Clone()
m1["2"] = "2"
m1 := m.Clone()
m1["2"] = "2"
g.Dump(m.Clone())
g.Dump(m1)
g.Dump(m.Clone())
g.Dump(m1)
}

View File

@ -1,39 +1,39 @@
package main
import (
"sync"
"time"
"fmt"
"fmt"
"sync"
"time"
)
// 验证 map 的delete方法是否并发安全
func main() {
// 创建一个初始化的map
m := make(map[int]int)
for i := 0; i < 10000; i++ {
m[i] = i
}
// 创建一个初始化的map
m := make(map[int]int)
for i := 0; i < 10000; i++ {
m[i] = i
}
fmt.Println("map size:", len(m))
fmt.Println("map size:", len(m))
wg := sync.WaitGroup{}
ev := make(chan struct{}, 0)
wg := sync.WaitGroup{}
ev := make(chan struct{}, 0)
// 创建10个并发的goroutine使用ev控制并发开始事件更容易模拟data race
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
<- ev
fmt.Println("start")
for i := 0; i < 10000; i++ {
delete(m, i)
}
wg.Done()
}()
}
// 创建10个并发的goroutine使用ev控制并发开始事件更容易模拟data race
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
<-ev
fmt.Println("start")
for i := 0; i < 10000; i++ {
delete(m, i)
}
wg.Done()
}()
}
time.Sleep(time.Second)
time.Sleep(time.Second)
close(ev)
wg.Wait()
close(ev)
wg.Wait()
}

View File

@ -1,25 +1,25 @@
package main
import (
"github.com/gogf/gf/g/container/gpool"
"fmt"
"time"
"fmt"
"github.com/gogf/gf/g/container/gpool"
"time"
)
func main () {
// 创建一个对象池过期时间为1000毫秒
p := gpool.New(1000, nil)
func main() {
// 创建一个对象池过期时间为1000毫秒
p := gpool.New(1000, nil)
// 从池中取一个对象返回nil及错误信息
fmt.Println(p.Get())
// 从池中取一个对象返回nil及错误信息
fmt.Println(p.Get())
// 丢一个对象到池中
p.Put(1)
// 丢一个对象到池中
p.Put(1)
// 重新从池中取一个对象返回1
fmt.Println(p.Get())
// 重新从池中取一个对象返回1
fmt.Println(p.Get())
// 等待1秒后重试发现对象已过期返回nil及错误信息
time.Sleep(time.Second)
fmt.Println(p.Get())
// 等待1秒后重试发现对象已过期返回nil及错误信息
time.Sleep(time.Second)
fmt.Println(p.Get())
}

View File

@ -1,33 +1,33 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/gtimer"
"time"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/container/gqueue"
"fmt"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/os/gtimer"
"time"
)
func main() {
q := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
v := gtime.Now().String()
q.Push(v)
fmt.Println("Push:", v)
})
q := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
v := gtime.Now().String()
q.Push(v)
fmt.Println("Push:", v)
})
// 3秒后关闭队列
gtimer.SetTimeout(3*time.Second, func() {
q.Close()
})
// 3秒后关闭队列
gtimer.SetTimeout(3*time.Second, func() {
q.Close()
})
// 消费者,不停读取队列数据并输出到终端
for {
if v := q.Pop(); v != nil {
fmt.Println(" Pop:", v)
} else {
break
}
}
// 消费者,不停读取队列数据并输出到终端
for {
if v := q.Pop(); v != nil {
fmt.Println(" Pop:", v)
} else {
break
}
}
}

View File

@ -1,27 +1,27 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/os/gtimer"
"time"
"fmt"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/os/gtimer"
"time"
)
func main() {
q := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
for i := 0; i < 10; i++ {
q.Push(i)
}
})
q := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
for i := 0; i < 10; i++ {
q.Push(i)
}
})
// 消费者,不停读取队列数据并输出到终端
for {
if v := q.Pop(); v != nil {
fmt.Println(" Pop:", v)
} else {
break
}
}
// 消费者,不停读取队列数据并输出到终端
for {
if v := q.Pop(); v != nil {
fmt.Println(" Pop:", v)
} else {
break
}
}
}

View File

@ -1,29 +1,29 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/os/gtimer"
"time"
"fmt"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/os/gtimer"
"time"
)
func main() {
queue := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
queue.Push(gtime.Now().String())
})
queue := gqueue.New()
// 数据生产者每隔1秒往队列写数据
gtimer.SetInterval(time.Second, func() {
queue.Push(gtime.Now().String())
})
// 消费者,不停读取队列数据并输出到终端
for {
select {
case v := <-queue.C:
if v != nil {
fmt.Println(v)
} else {
return
}
}
}
// 消费者,不停读取队列数据并输出到终端
for {
select {
case v := <-queue.C:
if v != nil {
fmt.Println(v)
} else {
return
}
}
}
}

View File

@ -1,27 +1,27 @@
package main
import (
"github.com/gogf/gf/g/container/gring"
"fmt"
"fmt"
"github.com/gogf/gf/g/container/gring"
)
func main() {
r1 := gring.New(10)
for i := 0; i < 5; i ++ {
r1.Set(i).Next()
}
fmt.Println("Len:", r1.Len())
fmt.Println("Cap:", r1.Cap())
fmt.Println(r1.SlicePrev())
fmt.Println(r1.SliceNext())
r1 := gring.New(10)
for i := 0; i < 5; i++ {
r1.Set(i).Next()
}
fmt.Println("Len:", r1.Len())
fmt.Println("Cap:", r1.Cap())
fmt.Println(r1.SlicePrev())
fmt.Println(r1.SliceNext())
r2 := gring.New(10)
for i := 0; i < 10; i ++ {
r2.Set(i).Next()
}
fmt.Println("Len:", r2.Len())
fmt.Println("Cap:", r2.Cap())
fmt.Println(r2.SlicePrev())
fmt.Println(r2.SliceNext())
r2 := gring.New(10)
for i := 0; i < 10; i++ {
r2.Set(i).Next()
}
fmt.Println("Len:", r2.Len())
fmt.Println("Cap:", r2.Cap())
fmt.Println(r2.SlicePrev())
fmt.Println(r2.SliceNext())
}

View File

@ -1,57 +1,57 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/container/gring"
"fmt"
"github.com/gogf/gf/g/container/gring"
)
type Player struct {
position int // 位置
alive bool // 是否存活
position int // 位置
alive bool // 是否存活
}
const (
playerCount = 41 // 玩家人数
startPos = 1 // 开始报数位置
playerCount = 41 // 玩家人数
startPos = 1 // 开始报数位置
)
var (
deadline = 3
deadline = 3
)
func main() {
// 关闭并发安全,当前场景没有必要
r := gring.New(playerCount, false)
// 关闭并发安全,当前场景没有必要
r := gring.New(playerCount, false)
// 设置所有玩家初始值
for i := 1; i <= playerCount; i++ {
r.Put(&Player{i, true})
}
// 设置所有玩家初始值
for i := 1; i <= playerCount; i++ {
r.Put(&Player{i, true})
}
// 如果开始报数的位置不为1则设置开始位置
if startPos > 1 {
r.Move(startPos - 1)
}
// 如果开始报数的位置不为1则设置开始位置
if startPos > 1 {
r.Move(startPos - 1)
}
counter := 1 // 报数从1开始因为下面的循环从第二个开始计算
deadCount := 0 // 死亡人数初始值为0
counter := 1 // 报数从1开始因为下面的循环从第二个开始计算
deadCount := 0 // 死亡人数初始值为0
// 直到所有人都死亡,否则循环一直执行
for deadCount < playerCount {
// 跳到下一个人
r.Next()
// 直到所有人都死亡,否则循环一直执行
for deadCount < playerCount {
// 跳到下一个人
r.Next()
// 如果是活着的人,则报数
if r.Val().(*Player).alive {
counter++
}
// 如果是活着的人,则报数
if r.Val().(*Player).alive {
counter++
}
// 如果报数为deadline则此人淘汰出局
if counter == deadline {
r.Val().(*Player).alive = false
fmt.Printf("Player %d died!\n", r.Val().(*Player).position)
deadCount++
counter = 0
}
}
}
// 如果报数为deadline则此人淘汰出局
if counter == deadline {
r.Val().(*Player).alive = false
fmt.Printf("Player %d died!\n", r.Val().(*Player).position)
deadCount++
counter = 0
}
}
}

View File

@ -1,52 +1,52 @@
package main
import (
"github.com/gogf/gf/g/container/gset"
"fmt"
"fmt"
"github.com/gogf/gf/g/container/gset"
)
func main() {
// 创建一个非并发安全的集合对象
s := gset.New(false)
// 创建一个非并发安全的集合对象
s := gset.New(false)
// 添加数据项
s.Add(1)
// 添加数据项
s.Add(1)
// 批量添加数据项
s.BatchAdd([]interface{}{1, 2, 3})
// 批量添加数据项
s.BatchAdd([]interface{}{1, 2, 3})
// 集合数据项大小
fmt.Println(s.Size())
// 集合数据项大小
fmt.Println(s.Size())
// 集合中是否存在指定数据项
fmt.Println(s.Contains(2))
// 集合中是否存在指定数据项
fmt.Println(s.Contains(2))
// 返回数据项slice
fmt.Println(s.Slice())
// 返回数据项slice
fmt.Println(s.Slice())
// 删除数据项
s.Remove(3)
// 删除数据项
s.Remove(3)
// 遍历数据项
s.Iterator(func(v interface{}) bool {
fmt.Println("Iterator:", v)
return true
})
// 遍历数据项
s.Iterator(func(v interface{}) bool {
fmt.Println("Iterator:", v)
return true
})
// 将集合转换为字符串
fmt.Println(s.String())
// 将集合转换为字符串
fmt.Println(s.String())
// 并发安全写锁操作
s.LockFunc(func(m map[interface{}]struct{}) {
m[4] = struct{}{}
})
// 并发安全写锁操作
s.LockFunc(func(m map[interface{}]struct{}) {
m[4] = struct{}{}
})
// 并发安全读锁操作
s.RLockFunc(func(m map[interface{}]struct{}) {
fmt.Println(m)
})
// 并发安全读锁操作
s.RLockFunc(func(m map[interface{}]struct{}) {
fmt.Println(m)
})
// 清空集合
s.Clear()
fmt.Println(s.Size())
// 清空集合
s.Clear()
fmt.Println(s.Size())
}

View File

@ -1,20 +1,20 @@
package main
import (
"github.com/gogf/gf/g/container/gtype"
"fmt"
"fmt"
"github.com/gogf/gf/g/container/gtype"
)
func main() {
// 创建一个Int型的并发安全基本类型对象
i := gtype.NewInt()
// 创建一个Int型的并发安全基本类型对象
i := gtype.NewInt()
// 设置值
i.Set(10)
// 设置值
i.Set(10)
// 获取值
fmt.Println(i.Val())
// 获取值
fmt.Println(i.Val())
// (整型/浮点型有效)数值 增加/删除 delta
fmt.Println(i.Add(-1))
// (整型/浮点型有效)数值 增加/删除 delta
fmt.Println(i.Add(-1))
}

View File

@ -1,36 +1,36 @@
package main
import (
"github.com/gogf/gf/g"
"fmt"
"fmt"
"github.com/gogf/gf/g"
)
func main() {
var v g.Var
var v g.Var
v.Set("123")
v.Set("123")
fmt.Println(v.Val())
fmt.Println(v.Val())
// 基本类型转换
fmt.Println(v.Int())
fmt.Println(v.Uint())
fmt.Println(v.Float64())
// 基本类型转换
fmt.Println(v.Int())
fmt.Println(v.Uint())
fmt.Println(v.Float64())
// slice转换
fmt.Println(v.Ints())
fmt.Println(v.Floats())
fmt.Println(v.Strings())
// slice转换
fmt.Println(v.Ints())
fmt.Println(v.Floats())
fmt.Println(v.Strings())
// struct转换
type Score struct {
Value int
}
s := new(Score)
v.Struct(s)
fmt.Println(s)
// struct转换
type Score struct {
Value int
}
s := new(Score)
v.Struct(s)
fmt.Println(s)
// 只读接口
r := v.ReadOnly()
fmt.Println(r.String())
// 只读接口
r := v.ReadOnly()
fmt.Println(r.String())
}

View File

@ -1,10 +1,10 @@
package main
import (
"fmt"
"time"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/database/gdb"
"time"
)
// 本文件用于gf框架的mysql数据库操作示例不作为单元测试使用
@ -12,90 +12,88 @@ import (
var db gdb.DB
// 初始化配置及创建数据库
func init () {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : "3306",
User : "root",
Pass : "12345678",
Name : "test",
Type : "mysql",
Role : "master",
Charset : "utf8",
})
db, _ = gdb.New()
func init() {
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, _ = gdb.New()
//gins.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/geg/frame")
//db = g.Database()
//gins.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/geg/frame")
//db = g.Database()
//gdb.SetConfig(gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : 3306,
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
//})
//db, _ = gdb.Instance()
//gdb.SetConfig(gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : 3306,
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
//})
//db, _ = gdb.Instance()
//gdb.SetConfig(gdb.Config {
// "default" : gdb.ConfigGroup {
// gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.2",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.3",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.4",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// },
//})
//db, _ = gdb.Instance()
//gdb.SetConfig(gdb.Config {
// "default" : gdb.ConfigGroup {
// gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.2",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.3",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.4",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Priority : 100,
// },
// },
//})
//db, _ = gdb.Instance()
}
// 创建测试数据库
func create() {
fmt.Println("create:")
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS test")
if (err != nil) {
fmt.Println(err)
}
fmt.Println("create:")
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS test")
if err != nil {
fmt.Println(err)
}
s := `
s := `
CREATE TABLE IF NOT EXISTS user (
uid INT(10) UNSIGNED AUTO_INCREMENT,
name VARCHAR(45),
@ -104,12 +102,12 @@ func create() {
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
_, err = db.Exec(s)
if err != nil {
fmt.Println(err)
}
s = `
s = `
CREATE TABLE IF NOT EXISTS user_detail (
uid INT(10) UNSIGNED AUTO_INCREMENT,
site VARCHAR(255),
@ -119,419 +117,416 @@ func create() {
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if (err != nil) {
fmt.Println(err)
}
fmt.Println()
_, err = db.Exec(s)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据写入
func insert() {
fmt.Println("insert:")
r, err := db.Insert("user", gdb.Map {
"name": "john",
})
if err == nil {
uid, err2 := r.LastInsertId()
if err2 == nil {
r, err = db.Insert("user_detail", gdb.Map {
"uid" : uid,
"site" : "http://johng.cn",
})
if err == nil {
fmt.Printf("uid: %d\n", uid)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err2)
}
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("insert:")
r, err := db.Insert("user", gdb.Map{
"name": "john",
})
if err == nil {
uid, err2 := r.LastInsertId()
if err2 == nil {
r, err = db.Insert("user_detail", gdb.Map{
"uid": uid,
"site": "http://johng.cn",
})
if err == nil {
fmt.Printf("uid: %d\n", uid)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err2)
}
} else {
fmt.Println(err)
}
fmt.Println()
}
// 基本sql查询
func query() {
fmt.Println("query:")
list, err := db.GetAll("select * from user limit 2")
if err == nil {
fmt.Println(list)
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("query:")
list, err := db.GetAll("select * from user limit 2")
if err == nil {
fmt.Println(list)
} else {
fmt.Println(err)
}
fmt.Println()
}
// replace into
func replace() {
fmt.Println("replace:")
r, err := db.Save("user", gdb.Map {
"uid" : 1,
"name" : "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("replace:")
r, err := db.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据保存
func save() {
fmt.Println("save:")
r, err := db.Save("user", gdb.Map {
"uid" : 1,
"name" : "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("save:")
r, err := db.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 批量写入
func batchInsert() {
fmt.Println("batchInsert:")
_, err := db.BatchInsert("user", gdb.List {
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}, 10)
if err != nil {
fmt.Println(err)
}
fmt.Println()
fmt.Println("batchInsert:")
_, err := db.BatchInsert("user", gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}, 10)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update1() {
fmt.Println("update1:")
r, err := db.Update("user", gdb.Map {"name": "john1"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("update1:")
r, err := db.Update("user", gdb.Map{"name": "john1"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update2() {
fmt.Println("update2:")
r, err := db.Update("user", gdb.Map{"name" : "john6"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("update2:")
r, err := db.Update("user", gdb.Map{"name": "john6"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update3() {
fmt.Println("update3:")
r, err := db.Update("user", "name=?", "uid=?", "john2", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("update3:")
r, err := db.Update("user", "name=?", "uid=?", "john2", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作1
func linkopSelect1() {
fmt.Println("linkopSelect1:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*, ud.site").Where("u.uid > ?", 1).Limit(0, 2).Select()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopSelect1:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*, ud.site").Where("u.uid > ?", 1).Limit(0, 2).Select()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作2
func linkopSelect2() {
fmt.Println("linkopSelect2:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.site").Where("u.uid=?", 1).One()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopSelect2:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.site").Where("u.uid=?", 1).One()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作3
func linkopSelect3() {
fmt.Println("linkopSelect3:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("ud.site").Where("u.uid=?", 1).Value()
if err == nil {
fmt.Println(r.String())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopSelect3:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("ud.site").Where("u.uid=?", 1).Value()
if err == nil {
fmt.Println(r.String())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询数量1
func linkopCount1() {
fmt.Println("linkopCount1:")
r, err := db.Table("user u").Fields("uid").LeftJoin("user_detail ud", "u.uid=ud.uid").Where("u.uid=?", 1).Count()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopCount1:")
r, err := db.Table("user u").Fields("uid").LeftJoin("user_detail ud", "u.uid=ud.uid").Where("u.uid=?", 1).Count()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 错误操作
func linkopUpdate1() {
fmt.Println("linkopUpdate1:")
r, err := db.Table("henghe_setting").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopUpdate1:")
r, err := db.Table("henghe_setting").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过Map指针方式传参方式
func linkopUpdate2() {
fmt.Println("linkopUpdate2:")
r, err := db.Table("user").Data(gdb.Map{"name" : "john2"}).Where("name=?", "john_1").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopUpdate2:")
r, err := db.Table("user").Data(gdb.Map{"name": "john2"}).Where("name=?", "john_1").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过字符串方式传参
func linkopUpdate3() {
fmt.Println("linkopUpdate3:")
r, err := db.Table("user").Data("name='john3'").Where("name=?", "john2").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopUpdate3:")
r, err := db.Table("user").Data("name='john3'").Where("name=?", "john2").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// Where条件使用Map
func linkopUpdate4() {
fmt.Println("linkopUpdate4:")
r, err := db.Table("user").Data(gdb.Map{"name" : "john11111"}).Where(g.Map{"uid" : 1}).Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopUpdate4:")
r, err := db.Table("user").Data(gdb.Map{"name": "john11111"}).Where(g.Map{"uid": 1}).Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量写入
func linkopBatchInsert1() {
fmt.Println("linkopBatchInsert1:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopBatchInsert1:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量写入,指定每批次写入的条数
func linkopBatchInsert2() {
fmt.Println("linkopBatchInsert2:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Batch(2).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopBatchInsert2:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Batch(2).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量保存
func linkopBatchSave() {
fmt.Println("linkopBatchSave:")
r, err := db.Table("user").Data(gdb.List{
{"uid":1, "name": "john_1"},
{"uid":2, "name": "john_2"},
{"uid":3, "name": "john_3"},
{"uid":4, "name": "john_4"},
}).Save()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
fmt.Println("linkopBatchSave:")
r, err := db.Table("user").Data(gdb.List{
{"uid": 1, "name": "john_1"},
{"uid": 2, "name": "john_2"},
{"uid": 3, "name": "john_3"},
{"uid": 4, "name": "john_4"},
}).Save()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 事务操作示例1
func transaction1() {
fmt.Println("transaction1:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Save("user", gdb.Map{
"uid" : 1,
"name" : "john",
})
tx.Rollback()
fmt.Println(r, err)
}
fmt.Println()
fmt.Println("transaction1:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
tx.Rollback()
fmt.Println(r, err)
}
fmt.Println()
}
// 事务操作示例2
func transaction2() {
fmt.Println("transaction2:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Table("user").Data(gdb.Map{"uid":1, "name": "john_1"}).Save()
tx.Commit()
fmt.Println(r, err)
}
fmt.Println()
fmt.Println("transaction2:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Table("user").Data(gdb.Map{"uid": 1, "name": "john_1"}).Save()
tx.Commit()
fmt.Println(r, err)
}
fmt.Println()
}
// 主从io复用测试在mysql中使用 show full processlist 查看链接信息
func keepPing() {
fmt.Println("keepPing:")
for {
fmt.Println("ping...")
err := db.PingMaster()
if err != nil {
fmt.Println(err)
return
}
err = db.PingSlave()
if err != nil {
fmt.Println(err)
return
}
time.Sleep(1*time.Second)
}
fmt.Println("keepPing:")
for {
fmt.Println("ping...")
err := db.PingMaster()
if err != nil {
fmt.Println(err)
return
}
err = db.PingSlave()
if err != nil {
fmt.Println(err)
return
}
time.Sleep(1 * time.Second)
}
}
// like语句查询
func likeQuery() {
fmt.Println("likeQuery:")
if r, err := db.Table("user").Where("name like ?", "%john%").Select(); err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println("likeQuery:")
if r, err := db.Table("user").Where("name like ?", "%john%").Select(); err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
}
// mapToStruct
func mapToStruct() {
type User struct {
Uid int
Name string
}
fmt.Println("mapToStruct:")
if r, err := db.Table("user").Where("uid=?", 1).One(); err == nil {
u := User{}
if err := r.ToStruct(&u); err == nil {
fmt.Println(r)
fmt.Println(u)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
type User struct {
Uid int
Name string
}
fmt.Println("mapToStruct:")
if r, err := db.Table("user").Where("uid=?", 1).One(); err == nil {
u := User{}
if err := r.ToStruct(&u); err == nil {
fmt.Println(r)
fmt.Println(u)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
fmt.Println("Func :", v.Func)
}
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
fmt.Println("Func :", v.Func)
}
}
func main() {
//data := g.Map{
// "nickname" : "john",
//}
//db.SetDebug(true)
//r, err := db.Table("user").Where("id=1").Data(data).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//data := g.Map{
// "nickname" : "john",
//}
//db.SetDebug(true)
//r, err := db.Table("user").Where("id=1").Data(data).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//data2 := g.Map{
// "adsys1" : "ss",
//}
//db.SetDebug(true)
//r, err := db.Table("cd_adsys").Where("adsys0=1").Data(data2).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//return
//db.SetDebug(true)
//r, err := db.Table("test").Where("id=1").One()
//fmt.Println(r["datetime"])
//fmt.Println(r["datetime"].Time().Date())
//fmt.Println(err)
//create()
//create()
//insert()
//query()
//replace()
//save()
//batchInsert()
//update1()
//update2()
//update3()
linkopSelect1()
//linkopSelect2()
//linkopSelect3()
//linkopCount1()
//linkopUpdate1()
//linkopUpdate2()
//linkopUpdate3()
//linkopUpdate4()
//
//transaction1()
//transaction2()
//
//keepPing()
//likeQuery()
//mapToStruct()
//getQueriedSqls()
}
//data2 := g.Map{
// "adsys1" : "ss",
//}
//db.SetDebug(true)
//r, err := db.Table("cd_adsys").Where("adsys0=1").Data(data2).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//return
//db.SetDebug(true)
//r, err := db.Table("test").Where("id=1").One()
//fmt.Println(r["datetime"])
//fmt.Println(r["datetime"].Time().Date())
//fmt.Println(err)
//create()
//create()
//insert()
//query()
//replace()
//save()
//batchInsert()
//update1()
//update2()
//update3()
linkopSelect1()
//linkopSelect2()
//linkopSelect3()
//linkopCount1()
//linkopUpdate1()
//linkopUpdate2()
//linkopUpdate3()
//linkopUpdate4()
//
//transaction1()
//transaction2()
//
//keepPing()
//likeQuery()
//mapToStruct()
//getQueriedSqls()
}

View File

@ -1,51 +1,50 @@
package main
import (
"github.com/gogf/gf/g/database/gdb"
"fmt"
"github.com/gogf/gf/g/crypto/gaes"
"github.com/gogf/gf/g"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/crypto/gaes"
"github.com/gogf/gf/g/database/gdb"
)
func main() {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : "3306",
User : "root",
Pass : "123456",
Name : "test",
Type : "mysql",
Role : "master",
Charset : "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "123456",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
key := "0123456789123456"
key := "0123456789123456"
name := "john"
encryptedName, err := gaes.Encrypt([]byte(name), []byte(key))
if err != nil {
fmt.Println(err)
}
name := "john"
encryptedName, err := gaes.Encrypt([]byte(name), []byte(key))
if err != nil {
fmt.Println(err)
}
// 写入
r, err := db.Table("user").Data(g.Map{
"uid": 1,
"name": encryptedName,
}).Save()
if err != nil {
fmt.Println(err)
}
fmt.Println(r.RowsAffected())
// 写入
r, err := db.Table("user").Data(g.Map{
"uid" : 1,
"name" : encryptedName,
}).Save()
if err != nil {
fmt.Println(err)
}
fmt.Println(r.RowsAffected())
// 查询
one, err := db.Table("user").Where("name=?", encryptedName).One()
if err != nil {
fmt.Println(err)
}
fmt.Println(one.ToMap())
}
// 查询
one, err := db.Table("user").Where("name=?", encryptedName).One()
if err != nil {
fmt.Println(err)
}
fmt.Println(one.ToMap())
}

View File

@ -1,38 +1,38 @@
package main
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/util/gutil"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/util/gutil"
)
func main() {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : "3306",
User : "root",
Pass : "123456",
Name : "test",
Type : "mysql",
Role : "master",
Charset : "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "123456",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
// 执行2次查询并将查询结果缓存3秒并可执行缓存名称(可选)
for i := 0; i < 2; i++ {
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
}
// 执行2次查询并将查询结果缓存3秒并可执行缓存名称(可选)
for i := 0; i < 2; i++ {
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
}
// 执行更新操作,并清理指定名称的查询缓存
db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name" : "smith"}).Where("uid=?", 1).Update()
// 执行更新操作,并清理指定名称的查询缓存
db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("uid=?", 1).Update()
// 再次执行查询,启用查询缓存特性
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
}
// 再次执行查询,启用查询缓存特性
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
gutil.Dump(r.ToMap())
}

View File

@ -1,16 +1,16 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"fmt"
"github.com/gogf/gf/g"
)
func main() {
g.Config().AddPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/geg/frame")
if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil {
fmt.Println(r["uid"].Int())
fmt.Println(r["name"].String())
} else {
fmt.Println(err)
}
}
g.Config().AddPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/geg/frame")
if r, err := g.DB().Table("user").Where("uid=?", 1).One(); err == nil {
fmt.Println(r["uid"].Int())
fmt.Println(r["name"].String())
} else {
fmt.Println(err)
}
}

View File

@ -1,29 +1,29 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gtime"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gtime"
)
func main() {
db := g.Database()
db.SetDebug(true)
db := g.Database()
db.SetDebug(true)
//r, err := db.Table("user").Data("create_time", gtime.Now().String()).Insert()
//if err == nil {
// fmt.Println(r.LastInsertId())
//} else {
// panic(err)
//}
//r, err := db.Table("user").Data("create_time", gtime.Now().String()).Insert()
//if err == nil {
// fmt.Println(r.LastInsertId())
//} else {
// panic(err)
//}
r, err := db.Table("user").Data(g.Map{
"name" : "john",
"create_time" : gtime.Now().String(),
}).Insert()
if err == nil {
fmt.Println(r.LastInsertId())
} else {
panic(err)
}
}
r, err := db.Table("user").Data(g.Map{
"name": "john",
"create_time": gtime.Now().String(),
}).Insert()
if err == nil {
fmt.Println(r.LastInsertId())
} else {
panic(err)
}
}

View File

@ -1,36 +1,36 @@
package main
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/os/glog"
)
func main() {
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
glog.SetPath("/tmp")
db.SetDebug(true)
// 执行3条SQL查询
for i := 1; i <= 3; i++ {
db.Table("user").Where("uid=?", i).One()
}
// 构造一条错误查询
db.Table("user").Where("no_such_field=?", "just_test").One()
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, err := gdb.New()
if err != nil {
panic(err)
}
glog.SetPath("/tmp")
db.SetDebug(true)
// 执行3条SQL查询
for i := 1; i <= 3; i++ {
db.Table("user").Where("uid=?", i).One()
}
// 构造一条错误查询
db.Table("user").Where("no_such_field=?", "just_test").One()
db.Table("user").Data(g.Map{"name":"smith"}).Where("uid=?", 1).Save()
db.Table("user").Data(g.Map{"name": "smith"}).Where("uid=?", 1).Save()
//db.PrintQueriedSqls()
}
//db.PrintQueriedSqls()
}

View File

@ -1,36 +1,36 @@
package main
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/database/gdb"
"fmt"
"github.com/gogf/gf/g/encoding/gparser"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/encoding/gparser"
)
func main() {
gdb.AddDefaultConfigNode(gdb.ConfigNode {
Host : "127.0.0.1",
Port : "3306",
User : "root",
Pass : "12345678",
Name : "test",
Type : "mysql",
Role : "master",
Charset : "utf8",
})
db := g.DB()
one, err := db.Table("user").Where("id=?", 1).One()
if err != nil {
panic(err)
}
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db := g.DB()
one, err := db.Table("user").Where("id=?", 1).One()
if err != nil {
panic(err)
}
// 使用内置方法转换为json/xml
fmt.Println(one.ToJson())
fmt.Println(one.ToXml())
// 使用内置方法转换为json/xml
fmt.Println(one.ToJson())
fmt.Println(one.ToXml())
// 自定义方法方法转换为json/xml
jsonContent, _ := gparser.VarToJson(one.ToMap())
fmt.Println(string(jsonContent))
xmlContent, _ := gparser.VarToXml(one.ToMap())
fmt.Println(string(xmlContent))
}
// 自定义方法方法转换为json/xml
jsonContent, _ := gparser.VarToJson(one.ToMap())
fmt.Println(string(jsonContent))
xmlContent, _ := gparser.VarToXml(one.ToMap())
fmt.Println(string(xmlContent))
}

View File

@ -1,24 +1,24 @@
package main
import (
"github.com/gogf/gf/g"
"time"
"github.com/gogf/gf/g"
"time"
)
func main() {
db := g.DB()
db.SetMaxIdleConns(10)
db.SetMaxOpenConns(10)
db.SetConnMaxLifetime(10)
db := g.DB()
db.SetMaxIdleConns(10)
db.SetMaxOpenConns(10)
db.SetConnMaxLifetime(10)
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
for {
for i := 0; i < 10; i++ {
go db.Table("user").All()
}
time.Sleep(time.Second)
}
for {
for i := 0; i < 10; i++ {
go db.Table("user").All()
}
time.Sleep(time.Second)
}
}
}

View File

@ -1,17 +1,17 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"fmt"
"github.com/gogf/gf/g"
)
func main() {
db := g.DB()
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
db := g.DB()
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
r, _ := db.Table("test").Where("id IN (?)", []interface{}{1, 2}).All()
if r != nil {
fmt.Println(r.ToList())
}
}
r, _ := db.Table("test").Where("id IN (?)", []interface{}{1, 2}).All()
if r != nil {
fmt.Println(r.ToList())
}
}

View File

@ -44,4 +44,4 @@ package main
// if _, err := db.Exec(sql); err != nil {
// fmt.Println(err)
// }
//}
//}

View File

@ -1,20 +1,19 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/database/gredis"
"fmt"
"github.com/gogf/gf/g/database/gredis"
"github.com/gogf/gf/g/util/gconv"
)
// 使用原生gredis.New操作redis但是注意需要自己调用Close方法关闭redis链接池
func main() {
redis := gredis.New(gredis.Config{
Host : "127.0.0.1",
Port : 6379,
})
defer redis.Close()
redis.Do("SET", "k", "v")
v, _ := redis.Do("GET", "k")
fmt.Println(gconv.String(v))
redis := gredis.New(gredis.Config{
Host: "127.0.0.1",
Port: 6379,
})
defer redis.Close()
redis.Do("SET", "k", "v")
v, _ := redis.Do("GET", "k")
fmt.Println(gconv.String(v))
}

View File

@ -1,15 +1,14 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
)
// 使用框架封装的g.Redis()方法获得redis操作对象单例不需要开发者显示调用Close方法
func main() {
g.Redis().Do("SET", "k", "v")
v, _ := g.Redis().Do("GET", "k")
fmt.Println(gconv.String(v))
g.Redis().Do("SET", "k", "v")
v, _ := g.Redis().Do("GET", "k")
fmt.Println(gconv.String(v))
}

View File

@ -1,16 +1,15 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
)
func main() {
conn := g.Redis().Conn()
defer conn.Close()
conn.Do("SET", "k", "v")
v, _ := conn.Do("GET", "k")
fmt.Println(gconv.String(v))
conn := g.Redis().Conn()
defer conn.Close()
conn.Do("SET", "k", "v")
v, _ := conn.Do("GET", "k")
fmt.Println(gconv.String(v))
}

View File

@ -1,21 +1,20 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
)
func main() {
conn := g.Redis().Conn()
defer conn.Close()
conn.Send("SET", "foo", "bar")
conn.Send("GET", "foo")
conn.Flush()
// reply from SET
conn.Receive()
// reply from GET
v, _ := conn.Receive()
fmt.Println(gconv.String(v))
conn := g.Redis().Conn()
defer conn.Close()
conn.Send("SET", "foo", "bar")
conn.Send("GET", "foo")
conn.Flush()
// reply from SET
conn.Receive()
// reply from GET
v, _ := conn.Receive()
fmt.Println(gconv.String(v))
}

View File

@ -1,24 +1,23 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/util/gconv"
)
func main() {
conn := g.Redis().Conn()
defer conn.Close()
_, err := conn.Do("SUBSCRIBE", "channel")
if err != nil {
panic(err)
}
for {
reply, err := conn.Receive()
if err != nil {
panic(err)
}
fmt.Println(gconv.Strings(reply))
}
conn := g.Redis().Conn()
defer conn.Close()
_, err := conn.Do("SUBSCRIBE", "channel")
if err != nil {
panic(err)
}
for {
reply, err := conn.Receive()
if err != nil {
panic(err)
}
fmt.Println(gconv.Strings(reply))
}
}

View File

@ -1,18 +1,15 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/encoding/gbase64"
"fmt"
"github.com/gogf/gf/g/encoding/gbase64"
)
func main() {
s := "john"
b := gbase64.Encode(s)
c, e := gbase64.Decode(b)
fmt.Println(b)
fmt.Println(c)
fmt.Println(e)
s := "john"
b := gbase64.Encode(s)
c, e := gbase64.Decode(b)
fmt.Println(b)
fmt.Println(c)
fmt.Println(e)
}

View File

@ -1,61 +1,61 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/encoding/gbinary"
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
"github.com/gogf/gf/g/os/glog"
)
func main() {
// 使用gbinary.Encoded对基本数据类型进行二进制打包
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
glog.Error(err)
} else {
fmt.Println(buffer)
}
// 使用gbinary.Encoded对基本数据类型进行二进制打包
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
glog.Error(err)
} else {
fmt.Println(buffer)
}
// 使用gbinary.Decode对整形二进制解包注意第二个及其后参数为字长确定的整形变量的指针地址字长确定的类型
// 例如int8/16/32/64、uint8/16/32/64、float32/64
// 这里的1.01默认为float64类型(64位系统下)
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
glog.Error(err)
} else {
var i1 int8
var i2 int16
var f3 float64
if err := gbinary.Decode(buffer, &i1, &i2, &f3); err != nil {
glog.Error(err)
} else {
fmt.Println(i1, i2, f3)
}
}
// 使用gbinary.Decode对整形二进制解包注意第二个及其后参数为字长确定的整形变量的指针地址字长确定的类型
// 例如int8/16/32/64、uint8/16/32/64、float32/64
// 这里的1.01默认为float64类型(64位系统下)
if buffer, err := gbinary.Encode(18, 300, 1.01); err != nil {
glog.Error(err)
} else {
var i1 int8
var i2 int16
var f3 float64
if err := gbinary.Decode(buffer, &i1, &i2, &f3); err != nil {
glog.Error(err)
} else {
fmt.Println(i1, i2, f3)
}
}
// 编码/解析 int自动识别变量长度
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(1)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(300)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(70000)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(2000000000)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(500000000000)))
// 编码/解析 int自动识别变量长度
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(1)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(300)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(70000)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(2000000000)))
fmt.Println(gbinary.DecodeToInt(gbinary.EncodeInt(500000000000)))
// 编码/解析 uint自动识别变量长度
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(1)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(300)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(70000)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(2000000000)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(500000000000)))
// 编码/解析 uint自动识别变量长度
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(1)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(300)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(70000)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(2000000000)))
fmt.Println(gbinary.DecodeToUint(gbinary.EncodeUint(500000000000)))
// 编码/解析 int8/16/32/64
fmt.Println(gbinary.DecodeToInt8(gbinary.EncodeInt8(int8(100))))
fmt.Println(gbinary.DecodeToInt16(gbinary.EncodeInt16(int16(100))))
fmt.Println(gbinary.DecodeToInt32(gbinary.EncodeInt32(int32(100))))
fmt.Println(gbinary.DecodeToInt64(gbinary.EncodeInt64(int64(100))))
// 编码/解析 int8/16/32/64
fmt.Println(gbinary.DecodeToInt8(gbinary.EncodeInt8(int8(100))))
fmt.Println(gbinary.DecodeToInt16(gbinary.EncodeInt16(int16(100))))
fmt.Println(gbinary.DecodeToInt32(gbinary.EncodeInt32(int32(100))))
fmt.Println(gbinary.DecodeToInt64(gbinary.EncodeInt64(int64(100))))
// 编码/解析 uint8/16/32/64
fmt.Println(gbinary.DecodeToUint8(gbinary.EncodeUint8(uint8(100))))
fmt.Println(gbinary.DecodeToUint16(gbinary.EncodeUint16(uint16(100))))
fmt.Println(gbinary.DecodeToUint32(gbinary.EncodeUint32(uint32(100))))
fmt.Println(gbinary.DecodeToUint64(gbinary.EncodeUint64(uint64(100))))
// 编码/解析 uint8/16/32/64
fmt.Println(gbinary.DecodeToUint8(gbinary.EncodeUint8(uint8(100))))
fmt.Println(gbinary.DecodeToUint16(gbinary.EncodeUint16(uint16(100))))
fmt.Println(gbinary.DecodeToUint32(gbinary.EncodeUint32(uint32(100))))
fmt.Println(gbinary.DecodeToUint64(gbinary.EncodeUint64(uint64(100))))
// 编码/解析 string
fmt.Println(gbinary.DecodeToString(gbinary.EncodeString("I'm string!")))
// 编码/解析 string
fmt.Println(gbinary.DecodeToString(gbinary.EncodeString("I'm string!")))
}

View File

@ -1,32 +1,32 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
)
func main() {
// 传感器状态0:已下线, 1:开启, 2:关闭, 3:待机
count := 100
status := 1
// 传感器状态0:已下线, 1:开启, 2:关闭, 3:待机
count := 100
status := 1
// 网关编码
bits := make([]gbinary.Bit, 0)
for i := 0; i < count; i++ {
bits = gbinary.EncodeBits(bits, status, 2)
}
buffer := gbinary.EncodeBitsToBytes(bits)
fmt.Println("buffer length:", len(buffer))
// 网关编码
bits := make([]gbinary.Bit, 0)
for i := 0; i < count; i++ {
bits = gbinary.EncodeBits(bits, status, 2)
}
buffer := gbinary.EncodeBitsToBytes(bits)
fmt.Println("buffer length:", len(buffer))
/* 上报过程忽略,这里只展示编码/解码示例 */
/* 上报过程忽略,这里只展示编码/解码示例 */
// 平台解码
alivecount := 0
sensorbits := gbinary.DecodeBytesToBits(buffer)
for i := 0; i < len(sensorbits); i += 2 {
if gbinary.DecodeBits(sensorbits[i:i+2]) == 1 {
alivecount++
}
}
fmt.Println("alived sensor:", alivecount)
// 平台解码
alivecount := 0
sensorbits := gbinary.DecodeBytesToBits(buffer)
for i := 0; i < len(sensorbits); i += 2 {
if gbinary.DecodeBits(sensorbits[i:i+2]) == 1 {
alivecount++
}
}
fmt.Println("alived sensor:", alivecount)
}

View File

@ -1,32 +1,32 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
"fmt"
"github.com/gogf/gf/g/encoding/gbinary"
)
func main() {
// Meta元数据文件数据结构[键名哈希64(64bit,8byte) 键名长度(8bit,1byte) 键值长度(24bit,3byte) 数据文件偏移量(40bit,5byte)](变长)
hash := 521369841259754125
klen := 12
vlen := 35535
offset := 80000000
// Meta元数据文件数据结构[键名哈希64(64bit,8byte) 键名长度(8bit,1byte) 键值长度(24bit,3byte) 数据文件偏移量(40bit,5byte)](变长)
hash := 521369841259754125
klen := 12
vlen := 35535
offset := 80000000
// 编码
bits := make([]gbinary.Bit, 0)
bits = gbinary.EncodeBits(bits, hash, 64)
bits = gbinary.EncodeBits(bits, klen, 8)
bits = gbinary.EncodeBits(bits, vlen, 24)
bits = gbinary.EncodeBits(bits, offset, 40)
buffer := gbinary.EncodeBitsToBytes(bits)
fmt.Println("meta length:", len(buffer))
// 编码
bits := make([]gbinary.Bit, 0)
bits = gbinary.EncodeBits(bits, hash, 64)
bits = gbinary.EncodeBits(bits, klen, 8)
bits = gbinary.EncodeBits(bits, vlen, 24)
bits = gbinary.EncodeBits(bits, offset, 40)
buffer := gbinary.EncodeBitsToBytes(bits)
fmt.Println("meta length:", len(buffer))
/* 文件存储及数据查询过程忽略,这里只展示元数据编码/解码示例 */
/* 文件存储及数据查询过程忽略,这里只展示元数据编码/解码示例 */
// 解码
metabits := gbinary.DecodeBytesToBits(buffer)
fmt.Println("hash :", gbinary.DecodeBits(metabits[0 : 64]))
fmt.Println("klen :", gbinary.DecodeBits(metabits[64 : 72]))
fmt.Println("vlen :", gbinary.DecodeBits(metabits[72 : 96]))
fmt.Println("offset:", gbinary.DecodeBits(metabits[96 : 136]))
// 解码
metabits := gbinary.DecodeBytesToBits(buffer)
fmt.Println("hash :", gbinary.DecodeBits(metabits[0:64]))
fmt.Println("klen :", gbinary.DecodeBits(metabits[64:72]))
fmt.Println("vlen :", gbinary.DecodeBits(metabits[72:96]))
fmt.Println("offset:", gbinary.DecodeBits(metabits[96:136]))
}

View File

@ -1,22 +1,19 @@
package main
import (
"fmt"
"github.com/gogf/gf/g"
"fmt"
"github.com/gogf/gf/g"
)
func main() {
fmt.Println(g.Config().Get("redis"))
fmt.Println(g.Config().Get("redis"))
type RedisConfig struct {
Disk string
Cache string
}
type RedisConfig struct {
Disk string
Cache string
}
redisCfg := new(RedisConfig)
fmt.Println(g.Config().GetToStruct("redis", redisCfg))
fmt.Println(redisCfg)
redisCfg := new(RedisConfig)
fmt.Println(g.Config().GetToStruct("redis", redisCfg))
fmt.Println(redisCfg)
}

View File

@ -1,20 +1,19 @@
package main
import (
"strconv"
"fmt"
"github.com/gogf/gf/g/encoding/ghash"
"fmt"
"github.com/gogf/gf/g/encoding/ghash"
"strconv"
)
func main () {
m := make(map[uint64]bool)
for i := 0; i < 100000000; i++ {
hash := ghash.BKDRHash64([]byte("key_" + strconv.Itoa(i)))
if _, ok := m[hash]; ok {
fmt.Printf("duplicated hash %d\n", hash)
} else {
m[hash] = true
}
}
func main() {
m := make(map[uint64]bool)
for i := 0; i < 100000000; i++ {
hash := ghash.BKDRHash64([]byte("key_" + strconv.Itoa(i)))
if _, ok := m[hash]; ok {
fmt.Printf("duplicated hash %d\n", hash)
} else {
m[hash] = true
}
}
}

View File

@ -1,15 +1,15 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/gtime"
"fmt"
"github.com/gogf/gf/g/encoding/gjson"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gtime"
)
func getByPattern() {
data :=
`{
data :=
`{
"users" : {
"count" : 100,
"list" : [
@ -18,143 +18,137 @@ func getByPattern() {
]
}
}`
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
fmt.Println("John Score:", j.GetFloat32("users.list.1.score"))
}
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
fmt.Println("John Score:", j.GetFloat32("users.list.1.score"))
}
}
// 当键名存在"."号时,检索优先级:键名->层级,因此不会引起歧义
func testMultiDots() {
data :=
`{
data :=
`{
"users" : {
"count" : 100
},
"users.count" : 101
}`
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
fmt.Println("Users Count:", j.GetInt("users.count"))
}
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
fmt.Println("Users Count:", j.GetInt("users.count"))
}
}
// 设置数据
func testSet() {
data :=
`{
data :=
`{
"users" : {
"count" : 100
}
}`
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
j.Set("users.count", 1)
j.Set("users.list", []string{"John", "小明"})
c, _ := j.ToJson()
fmt.Println(string(c))
}
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
j.Set("users.count", 1)
j.Set("users.list", []string{"John", "小明"})
c, _ := j.ToJson()
fmt.Println(string(c))
}
}
// 将Json数据转换为其他数据格式
func testConvert() {
data :=
`{
data :=
`{
"users" : {
"count" : 100,
"list" : ["John", "小明"]
}
}`
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
c, _ := j.ToJson()
fmt.Println("JSON:")
fmt.Println(string(c))
fmt.Println("======================")
j, err := gjson.DecodeToJson([]byte(data))
if err != nil {
glog.Error(err)
} else {
c, _ := j.ToJson()
fmt.Println("JSON:")
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("XML:")
c, _ = j.ToXmlIndent()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("XML:")
c, _ = j.ToXmlIndent()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("YAML:")
c, _ = j.ToYaml()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("YAML:")
c, _ = j.ToYaml()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("TOML:")
c, _ = j.ToToml()
fmt.Println(string(c))
}
fmt.Println("TOML:")
c, _ = j.ToToml()
fmt.Println(string(c))
}
}
func testSplitChar() {
var v interface{}
j := gjson.New(nil)
t1 := gtime.Nanosecond()
j.Set("a.b.c.d.e.f.g.h.i.j.k", 1)
t2 := gtime.Nanosecond()
fmt.Println(t2 - t1)
var v interface{}
j := gjson.New(nil)
t1 := gtime.Nanosecond()
j.Set("a.b.c.d.e.f.g.h.i.j.k", 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()
fmt.Println(v)
fmt.Println(t6 - t5)
t5 := gtime.Nanosecond()
v = j.Get("a.b.c.d.e.f.g.h.i.j.k")
t6 := gtime.Nanosecond()
fmt.Println(v)
fmt.Println(t6 - t5)
j.SetSplitChar('#')
j.SetSplitChar('#')
t7 := gtime.Nanosecond()
v = j.Get("a#b#c#d#e#f#g#h#i#j#k")
t8 := gtime.Nanosecond()
fmt.Println(v)
fmt.Println(t8 - t7)
t7 := gtime.Nanosecond()
v = j.Get("a#b#c#d#e#f#g#h#i#j#k")
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)
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)
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)
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)
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)
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)
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() {
testViolenceCheck()
}
testViolenceCheck()
}

View File

@ -1,14 +1,14 @@
package main
import (
"fmt"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/encoding/gparser"
"fmt"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/os/glog"
)
func getWithPattern1() {
data :=
`{
data :=
`{
"users" : {
"count" : 100,
"list" : [
@ -18,16 +18,16 @@ func getWithPattern1() {
}
}`
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
}
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
}
}
func getWithPattern2() {
data :=
`<?xml version="1.0" encoding="UTF-8"?>
data :=
`<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
@ -35,32 +35,32 @@ func getWithPattern2() {
<body>Don't forget me this weekend!</body>
</note>`
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
glog.Error(e)
} else {
fmt.Println("Heading:", p.GetString("note.heading"))
}
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
glog.Error(e)
} else {
fmt.Println("Heading:", p.GetString("note.heading"))
}
}
// 当键名存在"."号时,检索优先级:键名->层级,因此不会引起歧义
func multiDots1() {
data :=
`{
data :=
`{
"users" : {
"count" : 100
},
"users.count" : 101
}`
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("Users Count:", p.Get("users.count"))
}
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("Users Count:", p.Get("users.count"))
}
}
func multiDots2() {
data :=
`{
data :=
`{
"users" : {
"count" : {
"type1" : 1,
@ -69,150 +69,148 @@ func multiDots2() {
"count.type1" : 100
}
}`
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("Users Count:", p.Get("users.count.type1"))
fmt.Println("Users Count:", p.Get("users.count.type2"))
}
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
fmt.Println("Users Count:", p.Get("users.count.type1"))
fmt.Println("Users Count:", p.Get("users.count.type2"))
}
}
// 设置数据
func set1() {
data :=
`<?xml version="1.0" encoding="UTF-8"?>
data :=
`<?xml version="1.0" encoding="UTF-8"?>
<article>
<count>10</count>
<list><title>gf article1</title><content>gf content1</content></list>
<list><title>gf article2</title><content>gf content2</content></list>
<list><title>gf article3</title><content>gf content3</content></list>
</article>`
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
glog.Error(e)
} else {
p.Set("article.list.0", nil)
c, _ := p.ToJson()
fmt.Println(string(c))
// {"article":{"count":"10","list":[{"content":"gf content2","title":"gf article2"},{"content":"gf content3","title":"gf article3"}]}}
}
if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
glog.Error(e)
} else {
p.Set("article.list.0", nil)
c, _ := p.ToJson()
fmt.Println(string(c))
// {"article":{"count":"10","list":[{"content":"gf content2","title":"gf article2"},{"content":"gf content3","title":"gf article3"}]}}
}
}
func set2() {
data :=
`{
data :=
`{
"users" : {
"count" : 100
}
}`
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
p.Set("users.count", 1)
p.Set("users.list", []string{"John", "小明"})
c, _ := p.ToJson()
fmt.Println(string(c))
}
if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
glog.Error(e)
} else {
p.Set("users.count", 1)
p.Set("users.list", []string{"John", "小明"})
c, _ := p.ToJson()
fmt.Println(string(c))
}
}
func makeXml1() {
p := gparser.New()
p.Set("name", "john")
p.Set("age", 18)
p.Set("scores", map[string]int{
"语文" : 100,
"数学" : 100,
"英语" : 100,
})
c, _ := p.ToXmlIndent("simple-xml")
fmt.Println(string(c))
p := gparser.New()
p.Set("name", "john")
p.Set("age", 18)
p.Set("scores", map[string]int{
"语文": 100,
"数学": 100,
"英语": 100,
})
c, _ := p.ToXmlIndent("simple-xml")
fmt.Println(string(c))
}
func makeJson1() {
type Order struct {
Id int `json:"id"`
Price float32 `json:"price"`
}
p := gparser.New()
p.Set("orders.list.0", Order{1, 100})
p.Set("orders.list.1", Order{2, 666})
p.Set("orders.list.2", Order{3, 999.99})
fmt.Println("Order 1 Price:", p.Get("orders.list.1.price"))
c, _ := p.ToJson()
fmt.Println(string(c))
type Order struct {
Id int `json:"id"`
Price float32 `json:"price"`
}
p := gparser.New()
p.Set("orders.list.0", Order{1, 100})
p.Set("orders.list.1", Order{2, 666})
p.Set("orders.list.2", Order{3, 999.99})
fmt.Println("Order 1 Price:", p.Get("orders.list.1.price"))
c, _ := p.ToJson()
fmt.Println(string(c))
}
func makeJson2() {
p := gparser.New(map[string]string{
"k1" : "v1",
"k2" : "v2",
})
p.Set("k1.1", []int{1,2,3})
//p.Set("0.0.1", []int{1,2,3})
c, _ := p.ToJson()
fmt.Println(string(c))
p := gparser.New(map[string]string{
"k1": "v1",
"k2": "v2",
})
p.Set("k1.1", []int{1, 2, 3})
//p.Set("0.0.1", []int{1,2,3})
c, _ := p.ToJson()
fmt.Println(string(c))
}
func makeJson3() {
p := gparser.New([]string{"a"})
p.Set("0.0.0", []int{1,2,3})
c, _ := p.ToJson()
fmt.Println(string(c))
p := gparser.New([]string{"a"})
p.Set("0.0.0", []int{1, 2, 3})
c, _ := p.ToJson()
fmt.Println(string(c))
}
func toStruct1() {
type Info struct {
Name string
Url string
}
o := Info{}
p := gparser.New(map[string]string{
"Name" : "gf",
"Url" : "https://gitee.com/johng",
})
p.ToStruct(&o)
fmt.Println("Name:", o.Name)
fmt.Println("Url :", o.Url)
type Info struct {
Name string
Url string
}
o := Info{}
p := gparser.New(map[string]string{
"Name": "gf",
"Url": "https://gitee.com/johng",
})
p.ToStruct(&o)
fmt.Println("Name:", o.Name)
fmt.Println("Url :", o.Url)
}
func convert() {
p := gparser.New(map[string]string{
"name" : "gf",
"site" : "https://gitee.com/johng",
})
c, _ := p.ToJson()
fmt.Println("JSON:")
fmt.Println(string(c))
fmt.Println("======================")
p := gparser.New(map[string]string{
"name": "gf",
"site": "https://gitee.com/johng",
})
c, _ := p.ToJson()
fmt.Println("JSON:")
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("XML:")
c, _ = p.ToXmlIndent()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("XML:")
c, _ = p.ToXmlIndent()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("YAML:")
c, _ = p.ToYaml()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("YAML:")
c, _ = p.ToYaml()
fmt.Println(string(c))
fmt.Println("======================")
fmt.Println("TOML:")
c, _ = p.ToToml()
fmt.Println(string(c))
fmt.Println("TOML:")
c, _ = p.ToToml()
fmt.Println(string(c))
}
func remove1() {
p := gparser.New(map[string]string{
"k1" : "v1",
"k2" : "v2",
})
p.Set("0.0.0.0.0.0.0.0", []int{1,2,3})
p.Remove("0.0")
c, _ := p.ToJson()
fmt.Println(string(c))
p := gparser.New(map[string]string{
"k1": "v1",
"k2": "v2",
})
p.Set("0.0.0.0.0.0.0.0", []int{1, 2, 3})
p.Remove("0.0")
c, _ := p.ToJson()
fmt.Println(string(c))
}
func main() {
toStruct1()
}
toStruct1()
}

View File

@ -1,25 +1,25 @@
package demo
import (
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
)
func init() {
s := g.Server()
s.BindHandler("/apple", Apple)
s.BindHandler("/pen", Pen)
s.BindHandler("/apple-pen", ApplePen)
s := g.Server()
s.BindHandler("/apple", Apple)
s.BindHandler("/pen", Pen)
s.BindHandler("/apple-pen", ApplePen)
}
func Apple(r *ghttp.Request) {
r.Response.Write("Apple")
r.Response.Write("Apple")
}
func Pen(r *ghttp.Request) {
r.Response.Write("Pen")
r.Response.Write("Pen")
}
func ApplePen(r *ghttp.Request) {
r.Response.Write("Apple-Pen")
}
r.Response.Write("Apple-Pen")
}

View File

@ -1,16 +1,16 @@
package demo
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
)
type Order struct { }
type Order struct{}
func init() {
g.Server().BindObject("/{.struct}-{.method}", new(Order))
g.Server().BindObject("/{.struct}-{.method}", new(Order))
}
func (o *Order) List(r *ghttp.Request) {
r.Response.Write("List")
r.Response.Write("List")
}

View File

@ -1,12 +1,12 @@
package demo
import (
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/frame/gins"
"github.com/gogf/gf/g/frame/gins"
"github.com/gogf/gf/g/net/ghttp"
)
func init() {
ghttp.GetServer().BindHandler("/config", func (r *ghttp.Request) {
r.Response.Write(gins.Config().GetString("database.default.0.host"))
})
ghttp.GetServer().BindHandler("/config", func(r *ghttp.Request) {
r.Response.Write(gins.Config().GetString("database.default.0.host"))
})
}

View File

@ -1,16 +1,16 @@
package demo
import (
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/os/gtime"
)
func init() {
ghttp.GetServer().BindHandler("/cookie", Cookie)
ghttp.GetServer().BindHandler("/cookie", Cookie)
}
func Cookie(r *ghttp.Request) {
datetime := r.Cookie.Get("datetime")
r.Cookie.Set("datetime", gtime.Datetime())
r.Response.Write("datetime:" + datetime)
}
datetime := r.Cookie.Get("datetime")
r.Cookie.Set("datetime", gtime.Datetime())
r.Response.Write("datetime:" + datetime)
}

View File

@ -1,21 +1,20 @@
package demo
import (
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/net/ghttp"
)
type ControllerDomain struct {}
type ControllerDomain struct{}
// 初始化控制器对象并绑定操作到Web Server
func init() {
// 只有localhost域名下才能访问该对象
// 对应URL为http://localhost:8199/test/show
// 通过该地址将无法访问到内容http://127.0.0.1:8199/test/show
ghttp.GetServer().Domain("localhost").BindObject("/domain", &ControllerDomain{})
// 只有localhost域名下才能访问该对象
// 对应URL为http://localhost:8199/test/show
// 通过该地址将无法访问到内容http://127.0.0.1:8199/test/show
ghttp.GetServer().Domain("localhost").BindObject("/domain", &ControllerDomain{})
}
// 用于对象映射
func (d *ControllerDomain) Show(r *ghttp.Request) {
r.Response.Write("It's show time bibi!")
r.Response.Write("It's show time bibi!")
}

View File

@ -1,24 +1,24 @@
package demo
import (
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/frame/gmvc"
"github.com/gogf/gf/g/frame/gmvc"
"github.com/gogf/gf/g/net/ghttp"
)
type ControllerExit struct {
gmvc.Controller
gmvc.Controller
}
func (c *ControllerExit) Init(r *ghttp.Request) {
c.Controller.Init(r)
c.Response.Write("exit, it will not print \"show\"")
c.Request.Exit()
c.Controller.Init(r)
c.Response.Write("exit, it will not print \"show\"")
c.Request.Exit()
}
func (c *ControllerExit) Show() {
c.Response.Write("show")
c.Response.Write("show")
}
func init() {
ghttp.GetServer().BindController("/exit", &ControllerExit{})
ghttp.GetServer().BindController("/exit", &ControllerExit{})
}

View File

@ -1,19 +1,19 @@
package demo
import (
"github.com/gogf/gf/g/net/ghttp"
"fmt"
"fmt"
"github.com/gogf/gf/g/net/ghttp"
)
func Form(r *ghttp.Request) {
fmt.Println(r.GetPostMap())
fmt.Println(r.GetPostString("name"))
fmt.Println(r.GetPostString("age"))
fmt.Println(r.GetPostMap())
fmt.Println(r.GetPostString("name"))
fmt.Println(r.GetPostString("age"))
}
func FormShow(r *ghttp.Request) {
r.Response.Write(`
r.Response.Write(`
<html>
<head>
<title>表单提交</title>
@ -30,6 +30,6 @@ func FormShow(r *ghttp.Request) {
}
func init() {
ghttp.GetServer().BindHandler("/form", Form)
ghttp.GetServer().BindHandler("/form/show", FormShow)
}
ghttp.GetServer().BindHandler("/form", Form)
ghttp.GetServer().BindHandler("/form/show", FormShow)
}

View File

@ -3,7 +3,7 @@ package demo
import "github.com/gogf/gf/g/net/ghttp"
func init() {
ghttp.GetServer().BindHandler("/", func(r *ghttp.Request){
r.Response.Write("Hello World!")
})
}
ghttp.GetServer().BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World!")
})
}

View File

@ -1,33 +1,30 @@
package demo
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/frame/gmvc"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/frame/gmvc"
)
type Method struct {
gmvc.Controller
gmvc.Controller
}
func init() {
// 第三个参数指定主要注册的方法,其他方法不注册,方法名称会自动追加到给定路由后面,构成新路由
// 以下注册会中注册两个新路由: /method/name, /method/age
g.Server().BindController("/method", new(Method), "Name, Age")
// 绑定路由到指定的方法执行,以下注册只会注册一个路由: /method-name
g.Server().BindControllerMethod("/method-name", new(Method), "Name")
// 第三个参数指定主要注册的方法,其他方法不注册,方法名称会自动追加到给定路由后面,构成新路由
// 以下注册会中注册两个新路由: /method/name, /method/age
g.Server().BindController("/method", new(Method), "Name, Age")
// 绑定路由到指定的方法执行,以下注册只会注册一个路由: /method-name
g.Server().BindControllerMethod("/method-name", new(Method), "Name")
}
func (c *Method) Name() {
c.Response.Write("John")
c.Response.Write("John")
}
func (c *Method) Age() {
c.Response.Write("18")
c.Response.Write("18")
}
func (c *Method) Info() {
c.Response.Write("Info")
c.Response.Write("Info")
}

Some files were not shown because too many files have changed in this diff Show More