diff --git a/encoding/gjson/gjson.go b/encoding/gjson/gjson.go index 1a7e8efce..675a07656 100644 --- a/encoding/gjson/gjson.go +++ b/encoding/gjson/gjson.go @@ -22,8 +22,17 @@ import ( ) const ( - // Separator char for hierarchical data access. - defaultSplitChar = '.' + ContentTypeJson = `json` + ContentTypeJs = `js` + ContentTypeXml = `xml` + ContentTypeIni = `ini` + ContentTypeYaml = `yaml` + ContentTypeYml = `yml` + ContentTypeToml = `toml` +) + +const ( + defaultSplitChar = '.' // Separator char for hierarchical data access. ) // Json is the customized JSON struct. @@ -34,10 +43,11 @@ type Json struct { vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char. } -// Options for Json object creating. +// Options for Json object creating/loading. type Options struct { Safe bool // Mark this object is for in concurrent-safe usage. This is especially for Json object creating. - Tags string // Custom priority tags for decoding. Eg: "json,yaml,MyTag". This is especially for struct parsing into Json object. + Tags string // Custom priority tags for decoding, eg: "json,yaml,MyTag". This is especially for struct parsing into Json object. + Type string // Type specifies the data content type, eg: json, xml, yaml, toml, ini. StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64. } diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 412854b5c..b0a2194f1 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -21,6 +21,7 @@ import ( "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" ) @@ -30,7 +31,7 @@ import ( // The parameter `safe` specifies whether using this Json object in concurrent-safe context, // which is false in default. func New(data interface{}, safe ...bool) *Json { - return NewWithTag(data, "json", safe...) + return NewWithTag(data, ContentTypeJson, safe...) } // NewWithTag creates a Json object with any variable type of `data`, but `data` should be a map @@ -104,56 +105,73 @@ func Load(path string, safe ...bool) (*Json, error) { } else { path = p } - option := Options{} - if len(safe) > 0 && safe[0] { - option.Safe = true + options := Options{ + Type: gfile.Ext(path), } - return doLoadContentWithOptions(gfile.Ext(path), gfile.GetBytesWithCache(path), option) + 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{} + option := Options{ + Type: ContentTypeJson, + } if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOptions("json", gconv.Bytes(data), option) + 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{} + option := Options{ + Type: ContentTypeXml, + } if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOptions("xml", gconv.Bytes(data), option) + 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{} + option := Options{ + Type: ContentTypeIni, + } if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOptions("ini", gconv.Bytes(data), option) + 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{} + option := Options{ + Type: ContentTypeYaml, + } if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOptions("yaml", gconv.Bytes(data), option) + 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{} + option := Options{ + Type: ContentTypeToml, + } if len(safe) > 0 && safe[0] { option.Safe = true } - return doLoadContentWithOptions("toml", gconv.Bytes(data), option) + return doLoadContentWithOptions(gconv.Bytes(data), option) } // LoadContent creates a Json object from given content, it checks the data type of `content` @@ -179,11 +197,13 @@ func LoadContentType(dataType string, data interface{}, safe ...bool) (*Json, er if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { content = content[3:] } - option := Options{} - if len(safe) > 0 && safe[0] { - option.Safe = true + options := Options{ + Type: dataType, } - return doLoadContentWithOptions(dataType, content, option) + 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. @@ -195,7 +215,14 @@ func IsValidDataType(dataType string) bool { dataType = dataType[1:] } switch dataType { - case "json", "js", "xml", "yaml", "yml", "toml", "ini": + case + ContentTypeJson, + ContentTypeJs, + ContentTypeXml, + ContentTypeYaml, + ContentTypeYml, + ContentTypeToml, + ContentTypeIni: return true } return false @@ -206,10 +233,13 @@ func loadContentWithOptions(data interface{}, options Options) (*Json, error) { if len(content) == 0 { return NewWithOptions(nil, options), nil } - return loadContentTypeWithOptions(checkDataType(content), content, options) + if options.Type == "" { + options.Type = checkDataType(content) + } + return loadContentTypeWithOptions(content, options) } -func loadContentTypeWithOptions(dataType string, data interface{}, options Options) (*Json, error) { +func loadContentTypeWithOptions(data interface{}, options Options) (*Json, error) { content := gconv.Bytes(data) if len(content) == 0 { return NewWithOptions(nil, options), nil @@ -218,13 +248,13 @@ func loadContentTypeWithOptions(dataType string, data interface{}, options Optio if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { content = content[3:] } - return doLoadContentWithOptions(dataType, content, options) + 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(dataType string, data []byte, options Options) (*Json, error) { +func doLoadContentWithOptions(data []byte, options Options) (*Json, error) { var ( err error result interface{} @@ -232,34 +262,39 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J if len(data) == 0 { return NewWithOptions(nil, options), nil } - if dataType == "" { - dataType = checkDataType(data) + if options.Type == "" { + options.Type = checkDataType(data) } - switch dataType { - case "json", ".json", ".js": + options.Type = gstr.TrimLeft(options.Type, ".") + switch options.Type { + case ContentTypeJson, ContentTypeJs: - case "xml", ".xml": + case ContentTypeXml: if data, err = gxml.ToJson(data); err != nil { return nil, err } - case "yml", "yaml", ".yml", ".yaml": + case ContentTypeYaml, ContentTypeYml: if data, err = gyaml.ToJson(data); err != nil { return nil, err } - case "toml", ".toml": + case ContentTypeToml: if data, err = gtoml.ToJson(data); err != nil { return nil, err } - case "ini", ".ini": + case ContentTypeIni: if data, err = gini.ToJson(data); err != nil { return nil, err } default: - err = gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported type "%s" for loading`, dataType) + err = gerror.NewCodef( + gcode.CodeInvalidParameter, + `unsupported type "%s" for loading`, + options.Type, + ) } if err != nil { return nil, err @@ -268,7 +303,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J if options.StrNumber { decoder.UseNumber() } - if err := decoder.Decode(&result); err != nil { + if err = decoder.Decode(&result); err != nil { return nil, err } switch result.(type) { @@ -283,23 +318,23 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J // functions to load the content for certain content type. func checkDataType(content []byte) string { if json.Valid(content) { - return "json" + return ContentTypeJson } else if gregex.IsMatch(`^<.+>[\S\s]+<.+>\s*$`, content) { - return "xml" + 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 "yml" + 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 "toml" + 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 "ini" + return ContentTypeIni } else { return "" } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 61717784b..a7b33f9cc 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -170,7 +170,10 @@ func (r *Request) GetBodyString() string { // GetJson parses current request content as JSON format, and returns the JSON object. // Note that the request content is read from request BODY, not from any field of FORM. func (r *Request) GetJson() (*gjson.Json, error) { - return gjson.LoadJson(r.GetBody()) + return gjson.LoadWithOptions(r.GetBody(), gjson.Options{ + Type: gjson.ContentTypeJson, + StrNumber: true, + }) } // GetMap is an alias and convenient function for GetRequestMap.