gparser

gf框架针对常用的数据格式编码解析,提供了异常强大灵活的功能,由gparser模块提供,支持Go变量(interface{})、Struct、JSON、XML、YAML/YML、TOML数据格式之间的相互转换,支持按照层级进行数据检索访问、支持运行时动态新增/修改/删除层级变量(并发安全)等特性。gparser包使得对于未知数据结构、多维数组结构的访问、操作变得异常的简便。

使用方式:

  1. import "gitee.com/johng/gf/g/encoding/gparser"

方法列表

godoc.org/github.com/johng-cn/gf/g/encoding/gparser

  1. func VarToJson(value interface{}) ([]byte, error)
  2. func VarToJsonIndent(value interface{}) ([]byte, error)
  3. func VarToStruct(value interface{}, obj interface{}) error
  4. func VarToToml(value interface{}) ([]byte, error)
  5. func VarToXml(value interface{}, rootTag ...string) ([]byte, error)
  6. func VarToXmlIndent(value interface{}, rootTag ...string) ([]byte, error)
  7. func VarToYaml(value interface{}) ([]byte, error)
  8. type Parser
  9. func Load(path string) (*Parser, error)
  10. func LoadContent(data []byte, dataType ...string) (*Parser, error)
  11. func New(value interface{}, safe ...bool) *Parser
  12. func NewUnsafe(value ...interface{}) *Parser
  13. func (p *Parser) Append(pattern string, value interface{}) error
  14. func (p *Parser) Dump() error
  15. func (p *Parser) Get(pattern ...string) interface{}
  16. func (p *Parser) GetArray(pattern string) []interface{}
  17. func (p *Parser) GetBool(pattern string) bool
  18. func (p *Parser) GetFloat32(pattern string) float32
  19. func (p *Parser) GetFloat64(pattern string) float64
  20. func (p *Parser) GetFloats(pattern string) []float64
  21. func (p *Parser) GetInt(pattern string) int
  22. func (p *Parser) GetInt16(pattern string) int16
  23. func (p *Parser) GetInt32(pattern string) int32
  24. func (p *Parser) GetInt64(pattern string) int64
  25. func (p *Parser) GetInt8(pattern string) int8
  26. func (p *Parser) GetInterfaces(pattern string) []interface{}
  27. func (p *Parser) GetInts(pattern string) []int
  28. func (p *Parser) GetMap(pattern string) map[string]interface{}
  29. func (p *Parser) GetString(pattern string) string
  30. func (p *Parser) GetStrings(pattern string) []string
  31. func (p *Parser) GetTime(pattern string, format ...string) time.Time
  32. func (p *Parser) GetTimeDuration(pattern string) time.Duration
  33. func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error
  34. func (p *Parser) GetToVar(pattern string, v interface{}) error
  35. func (p *Parser) GetUint(pattern string) uint
  36. func (p *Parser) GetUint16(pattern string) uint16
  37. func (p *Parser) GetUint32(pattern string) uint32
  38. func (p *Parser) GetUint64(pattern string) uint64
  39. func (p *Parser) GetUint8(pattern string) uint8
  40. func (p *Parser) Len(pattern string) int
  41. func (p *Parser) Remove(pattern string) error
  42. func (p *Parser) Set(pattern string, value interface{}) error
  43. func (p *Parser) SetSplitChar(char byte)
  44. func (p *Parser) SetViolenceCheck(check bool)
  45. func (p *Parser) ToArray() []interface{}
  46. func (p *Parser) ToJson() ([]byte, error)
  47. func (p *Parser) ToJsonIndent() ([]byte, error)
  48. func (p *Parser) ToMap() map[string]interface{}
  49. func (p *Parser) ToStruct(o interface{}) error
  50. func (p *Parser) ToToml() ([]byte, error)
  51. func (p *Parser) ToXml(rootTag ...string) ([]byte, error)
  52. func (p *Parser) ToXmlIndent(rootTag ...string) ([]byte, error)
  53. func (p *Parser) ToYaml() ([]byte, error)

方法简要说明,

  1. LoadLoadContent方法支持根据文件及内容,生成gparser.Parser对象;
  2. New方法支持生成一个空的gparser.Parser对象,常用用于动态数据生成;
  3. New方法同时也支持按照给定的任意Go变量生成一个gparser.Parser对象;
  4. Get*相关方法支持按照层级检索数据,pattern参数中使用英文”.“号区分层级关系;
  5. Set方法支持按照层级新增/修改,给定的变量类型支持任意类型
  6. Remove方法支持按照层级删除变量,只需要给定pattern层级检索参数即可;
  7. To*相关方法支持将gparser.Parser对象生成为支持的数据格式字符串;
  8. VarTo*相关方法支持将任意的Go变量直接转换为支持的数据格式字符串;

使用示例

  1. 数据层级检索

    示例1,读取JSON:

    1. data :=
    2. `{
    3. "users" : {
    4. "count" : 100,
    5. "list" : [
    6. {"name" : "Ming", "score" : 60},
    7. {"name" : "John", "score" : 99.5}
    8. ]
    9. }
    10. }`
    11. if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
    12. glog.Error(e)
    13. } else {
    14. fmt.Println("John Score:", p.GetFloat32("users.list.1.score"))
    15. }

    可以看到我们可以通过英文”.”号实现非常方便的层级访问,针对于数组列表,索引从0开始,我们也可以通过”.”号访问其对应的索引项数据。

    示例2,读取XML:

    1. data :=
    2. `<?xml version="1.0" encoding="UTF-8"?>
    3. <note>
    4. <to>Tove</to>
    5. <from>Jani</from>
    6. <heading>Reminder</heading>
    7. <body>Don't forget me this weekend!</body>
    8. </note>`
    9. if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
    10. glog.Error(e)
    11. } else {
    12. fmt.Println("Heading:", p.GetString("note.heading"))
    13. }

    LoadContent方法的第二个参数指定内容的数据类型,可选值为(json,xml,yaml/yml,toml)。其他两种数据类型可自行测试,这里不再赘述。

  2. 处理键名本身带有层级符号”.”的情况

    当键名和层级在访问时存在pattern同名的情况,当然这并不是什么问题,以下是一个示例。

    1. data :=
    2. `{
    3. "users" : {
    4. "count" : 100
    5. },
    6. "users.count" : 101
    7. }`
    8. if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
    9. glog.Error(e)
    10. } else {
    11. p.SetViolenceCheck(true)
    12. fmt.Println("Users Count:", p.Get("users.count"))
    13. }

    运行之后打印出的结果为101。当键名存在”.”号时,检索优先级:键名->层级,因此并不会引起歧义。

    再来看一个例子:

    1. data :=
    2. `{
    3. "users" : {
    4. "count" : {
    5. "type1" : 1,
    6. "type2" : 2
    7. },
    8. "count.type1" : 100
    9. }
    10. }`
    11. if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
    12. glog.Error(e)
    13. } else {
    14. p.SetViolenceCheck(true)
    15. fmt.Println("Users Count:", p.Get("users.count.type1"))
    16. fmt.Println("Users Count:", p.Get("users.count.type2"))
    17. }

    执行后,输出结果为:

    1. 100
    2. 2

    看到了么,gparser会按照给定pattern对层级进行自动探测,检索时按照键名优先的原则进行匹配,并不会出现歧义冲突。

  1. 运行时动态修改数据

    1. data :=
    2. `{
    3. "users" : {
    4. "count" : 100
    5. }
    6. }`
    7. if p, e := gparser.LoadContent([]byte(data), "json"); e != nil {
    8. glog.Error(e)
    9. } else {
    10. p.Set("users.count", 2)
    11. p.Set("users.list", []string{"John", "小明"})
    12. c, _ := p.ToJson()
    13. fmt.Println(string(c))
    14. }

    修改count为2,并在users节点下新增增加list节点,节点类型为数组。
    执行后输出结果为:

    1. {"users":{"count":2,"list":["John","小明"]}}

    gparser包的数据运行时修改特性非常强大,在该特性的支持下,各种数据结构的编码/解析显得异常的灵活方便。

  2. 运行时动态删除变量

    我们再来看一个删除变量的例子:

    1. data :=
    2. `<?xml version="1.0" encoding="UTF-8"?>
    3. <article>
    4. <count>10</count>
    5. <list><title>gf article1</title><content>gf content1</content></list>
    6. <list><title>gf article2</title><content>gf content2</content></list>
    7. <list><title>gf article3</title><content>gf content3</content></list>
    8. </article>`
    9. if p, e := gparser.LoadContent([]byte(data), "xml"); e != nil {
    10. glog.Error(e)
    11. } else {
    12. p.Remove("article.list.0")
    13. c, _ := p.ToJson()
    14. fmt.Println(string(c))
    15. }

    以上程序输出结果为:

    1. {"article":{"count":"10","list":[{"content":"gf content2","title":"gf article2"},{"content":"gf content3","title":"gf article3"}]}}

    可以看到,使用Remove方法可以非常方便地根据pattern参数动态删除变量。在该示例中,我们删除了article.list数组的索引0数据项,并将XML转换为JSON数据格式返回。

  3. 动态生成指定格式的编码数据

    我们来动态生成一个XML,先来一个简单一点的。

    1. p := gparser.New()
    2. p.Set("name", "john")
    3. p.Set("age", 18)
    4. p.Set("scores", map[string]int{
    5. "语文" : 100,
    6. "数学" : 100,
    7. "英语" : 100,
    8. })
    9. c, _ := p.ToXmlIndent("simple-xml")
    10. fmt.Println(string(c))

    执行后,输出结果为:

    1. <simple-xml>
    2. <age>18</age>
    3. <name>john</name>
    4. <scores>
    5. <数学>100</数学>
    6. <英语>100</英语>
    7. <语文>100</语文>
    8. </scores>
    9. </simple-xml>

    可以看到,我们直接使用Set方式便创建了一个XML数据格式,根本就不需要struct有木有?!想要struct?当然也可以,请看下面的示例。

  1. 变量与struct相互转换

    示例1,struct转换为gparser.Parser对象,并转换输出为JSON格式:

    1. type Order struct {
    2. Id int `json:"id"`
    3. Price float32 `json:"price"`
    4. }
    5. p := gparser.New()
    6. p.Set("orders.list.0", Order{1, 100})
    7. p.Set("orders.list.1", Order{2, 666})
    8. p.Set("orders.list.2", Order{3, 999.99})
    9. fmt.Println("Order 1 Price:", p.Get("orders.list.1.price"))
    10. c, _ := p.ToJson()
    11. fmt.Println(string(c))

    执行后,输出结果为:

    1. Order 1 Price: 666
    2. {"orders":{"list":{"0":{"id":1,"price":100},"1":{"id":2,"price":666},"2":{"id":3,"price":999.99}}}}

    怎么样,有没有觉得很6?

    但是针对于struct处理,这里有一点需要特别说明的是,如果struct中的变量不对外公开,那么该变量同时也不能被gparser通过层级检索方式访问到。由于gparser的底层数据结构采用了json格式,如果struct同时定义了json tag,那么层级检索将会按照json tag进行检索访问,不支持struct的其他tag类型。

    示例2,变量转换为struct对象:

    1. type Info struct {
    2. Name string
    3. Url string
    4. }
    5. o := Info{}
    6. p := gparser.New(map[string]string {
    7. "Name" : "gf",
    8. "Url" : "https://gitee.com/johng",
    9. })
    10. p.ToStruct(&o)
    11. fmt.Println("Name:", o.Name)
    12. fmt.Println("Url :", o.Url)

    执行后,输出为:

    1. Name: gf
    2. Url : https://gitee.com/johng

    当然,也可以直接使用gparser.VarToStruct方法来进行直接转换。

  2. 数据格式相互转换

    由于只是演示数据格式的转换,咱们来个数据结构简单点的:

    1. p := gparser.New(map[string]string{
    2. "name" : "gf",
    3. "site" : "https://gitee.com/johng",
    4. })
    5. c, _ := p.ToJson()
    6. fmt.Println("JSON:")
    7. fmt.Println(string(c))
    8. fmt.Println("======================")
    9. fmt.Println("XML:")
    10. c, _ = p.ToXmlIndent()
    11. fmt.Println(string(c))
    12. fmt.Println("======================")
    13. fmt.Println("YAML:")
    14. c, _ = p.ToYaml()
    15. fmt.Println(string(c))
    16. fmt.Println("======================")
    17. fmt.Println("TOML:")
    18. c, _ = p.ToToml()
    19. fmt.Println(string(c))

    执行后,输出结果为:

    1. JSON:
    2. {"name":"gf","site":"https://gitee.com/johng"}
    3. ======================
    4. XML:
    5. <doc>
    6. <name>gf</name>
    7. <site>https://gitee.com/johng</site>
    8. </doc>
    9. ======================
    10. YAML:
    11. name: gf
    12. site: https://gitee.com/johng
    13. ======================
    14. TOML:
    15. name = "gf"
    16. site = "https://gitee.com/johng"

    可以看到,gparser包使得数据格式的转换变得异常的方便灵活。