Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(encoding/gjson): change data parameter from type any to []byte #3542

Merged
merged 7 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ linters-settings:
govet:
# Report about shadowed variables.
# Default: false
check-shadowing: true
# check-shadowing: true
# Settings per analyzer.
settings:
# Analyzer name, run `go tool vet help` to see all analyzers.
Expand Down
2 changes: 1 addition & 1 deletion contrib/config/kubecm/kubecm.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (c *Client) doUpdate(ctx context.Context, namespace string) (err error) {
)
}
var j *gjson.Json
if j, err = gjson.LoadContent(cm.Data[c.config.DataItem]); err != nil {
if j, err = gjson.LoadContent([]byte(cm.Data[c.config.DataItem])); err != nil {
return gerror.Wrapf(
err,
`parse config map item from %s[%s] failed`, c.config.ConfigMap, c.config.DataItem,
Expand Down
303 changes: 19 additions & 284 deletions encoding/gjson/gjson_api_new_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,10 @@
package gjson

import (
"bytes"
"reflect"

"github.com/gogf/gf/v2/encoding/gini"
"github.com/gogf/gf/v2/encoding/gproperties"
"github.com/gogf/gf/v2/encoding/gtoml"
"github.com/gogf/gf/v2/encoding/gxml"
"github.com/gogf/gf/v2/encoding/gyaml"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/internal/rwmutex"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)

Expand Down Expand Up @@ -57,16 +45,26 @@ func NewWithTag(data interface{}, tags string, safe ...bool) *Json {
// or slice for data access reason, or it will make no sense.
func NewWithOptions(data interface{}, options Options) *Json {
var j *Json
switch data.(type) {
case string, []byte:
if r, err := loadContentWithOptions(data, options); err == nil {
switch result := data.(type) {
case []byte:
if r, err := loadContentWithOptions(result, options); err == nil {
j = r
} else {
j = &Json{
p: &data,
c: byte(defaultSplitChar),
vc: false,
}
break
}
j = &Json{
p: &data,
c: byte(defaultSplitChar),
vc: false,
}
case string:
if r, err := loadContentWithOptions([]byte(result), options); err == nil {
j = r
break
}
j = &Json{
p: &data,
c: byte(defaultSplitChar),
vc: false,
}
default:
var (
Expand Down Expand Up @@ -98,266 +96,3 @@ func NewWithOptions(data interface{}, options Options) *Json {
j.mu = rwmutex.Create(options.Safe)
return j
}

// Load loads content from specified file `path`, and creates a Json object from its content.
func Load(path string, safe ...bool) (*Json, error) {
if p, err := gfile.Search(path); err != nil {
return nil, err
} else {
path = p
}
options := Options{
Type: ContentType(gfile.Ext(path)),
}
if len(safe) > 0 && safe[0] {
options.Safe = true
}
return doLoadContentWithOptions(gfile.GetBytesWithCache(path), options)
}

// LoadWithOptions creates a Json object from given JSON format content and options.
func LoadWithOptions(data interface{}, options Options) (*Json, error) {
return doLoadContentWithOptions(gconv.Bytes(data), options)
}

// LoadJson creates a Json object from given JSON format content.
func LoadJson(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeJson,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadXml creates a Json object from given XML format content.
func LoadXml(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeXml,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadIni creates a Json object from given INI format content.
func LoadIni(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeIni,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadYaml creates a Json object from given YAML format content.
func LoadYaml(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeYaml,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadToml creates a Json object from given TOML format content.
func LoadToml(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeToml,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadProperties creates a Json object from given TOML format content.
func LoadProperties(data interface{}, safe ...bool) (*Json, error) {
option := Options{
Type: ContentTypeProperties,
}
if len(safe) > 0 && safe[0] {
option.Safe = true
}
return doLoadContentWithOptions(gconv.Bytes(data), option)
}

// LoadContent creates a Json object from given content, it checks the data type of `content`
// automatically, supporting data content type as follows:
// JSON, XML, INI, YAML and TOML.
func LoadContent(data interface{}, safe ...bool) (*Json, error) {
content := gconv.Bytes(data)
if len(content) == 0 {
return New(nil, safe...), nil
}
return LoadContentType(checkDataType(content), content, safe...)
}

// LoadContentType creates a Json object from given type and content,
// supporting data content type as follows:
// JSON, XML, INI, YAML and TOML.
func LoadContentType(dataType ContentType, data interface{}, safe ...bool) (*Json, error) {
content := gconv.Bytes(data)
if len(content) == 0 {
return New(nil, safe...), nil
}
// ignore UTF8-BOM
if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF {
content = content[3:]
}
options := Options{
Type: dataType,
StrNumber: true,
}
if len(safe) > 0 && safe[0] {
options.Safe = true
}
return doLoadContentWithOptions(content, options)
}

// IsValidDataType checks and returns whether given `dataType` a valid data type for loading.
func IsValidDataType(dataType ContentType) bool {
if dataType == "" {
return false
}
if dataType[0] == '.' {
dataType = dataType[1:]
}
switch dataType {
case
ContentTypeJson,
ContentTypeJs,
ContentTypeXml,
ContentTypeYaml,
ContentTypeYml,
ContentTypeToml,
ContentTypeIni,
ContentTypeProperties:
return true
}
return false
}

func loadContentWithOptions(data interface{}, options Options) (*Json, error) {
content := gconv.Bytes(data)
if len(content) == 0 {
return NewWithOptions(nil, options), nil
}
if options.Type == "" {
options.Type = checkDataType(content)
}
return loadContentTypeWithOptions(content, options)
}

func loadContentTypeWithOptions(data interface{}, options Options) (*Json, error) {
content := gconv.Bytes(data)
if len(content) == 0 {
return NewWithOptions(nil, options), nil
}
// ignore UTF8-BOM
if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF {
content = content[3:]
}
return doLoadContentWithOptions(content, options)
}

// doLoadContent creates a Json object from given content.
// It supports data content type as follows:
// JSON, XML, INI, YAML and TOML.
func doLoadContentWithOptions(data []byte, options Options) (*Json, error) {
var (
err error
result interface{}
)
if len(data) == 0 {
return NewWithOptions(nil, options), nil
}
if options.Type == "" {
options.Type = checkDataType(data)
}
options.Type = ContentType(gstr.TrimLeft(
string(options.Type), "."),
)
switch options.Type {
case ContentTypeJson, ContentTypeJs:

case ContentTypeXml:
if data, err = gxml.ToJson(data); err != nil {
return nil, err
}

case ContentTypeYaml, ContentTypeYml:
if data, err = gyaml.ToJson(data); err != nil {
return nil, err
}

case ContentTypeToml:
if data, err = gtoml.ToJson(data); err != nil {
return nil, err
}

case ContentTypeIni:
if data, err = gini.ToJson(data); err != nil {
return nil, err
}
case ContentTypeProperties:
if data, err = gproperties.ToJson(data); err != nil {
return nil, err
}

default:
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupported type "%s" for loading`,
options.Type,
)
}
if err != nil {
return nil, err
}
decoder := json.NewDecoder(bytes.NewReader(data))
if options.StrNumber {
decoder.UseNumber()
}
if err = decoder.Decode(&result); err != nil {
return nil, err
}
switch result.(type) {
case string, []byte:
return nil, gerror.Newf(`json decoding failed for content: %s`, data)
}
return NewWithOptions(result, options), nil
}

// checkDataType automatically checks and returns the data type for `content`.
// Note that it uses regular expression for loose checking, you can use LoadXXX/LoadContentType
// functions to load the content for certain content type.
func checkDataType(content []byte) ContentType {
if json.Valid(content) {
return ContentTypeJson
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>\s*$`, content) {
return ContentTypeXml
} else if !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*"""[\s\S]+"""`, content) &&
!gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*'''[\s\S]+'''`, content) &&
((gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*\w+`, content)) ||
(gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`[\n\r]+[\w\-\s\t]+\s*:\s*\w+`, content))) {
return ContentTypeYaml
} else if !gregex.IsMatch(`^[\s\t\n\r]*;.+`, content) &&
!gregex.IsMatch(`[\s\t\n\r]+;.+`, content) &&
!gregex.IsMatch(`[\n\r]+[\s\t\w\-]+\.[\s\t\w\-]+\s*=\s*.+`, content) &&
(gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*".+"`, content) || gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*\w+`, content)) {
return ContentTypeToml
} else if gregex.IsMatch(`\[[\w\.]+\]`, content) &&
(gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*".+"`, content) || gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*\w+`, content)) {
// Must contain "[xxx]" section.
return ContentTypeIni
} else if gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*\w+`, content) {
return ContentTypeProperties
} else {
return ""
}
}
Loading
Loading