zoukankan      html  css  js  c++  java
  • Golang JSON操作汇总

    直接把结构体编码成json数据

    package main
    
    import (
        "encoding/json"
        "fmt"
        _ "os"
    )
    
    type Address struct {
        Type string
        City string
        Country string
    }
    
    type Card struct {
        Name string
        Age int
        Addresses []*Address
    }
    
    func main() {
        pa := &Address{"private", "Shanghai", "China"}
        pu := &Address{"work", "Beijing", "China"}
        c := Card{"Xin", 32, []*Address{pa, pu}}
    
        js, _ := json.Marshal(c)
        fmt.Printf("Json: %s", js)
    }
    

    利用json.Marshal,可将结构体转为JSON数据:

    Json: {"Name":"Xin","Age":32,"Addresses":[{"Type":"private","City":"Shanghai","Country":"China"},{"Type":"work","City":"Beijing","Country":"China"}]}
    

    json.MarshalIndent(c, "", " ")可以将json代码格式化:

    Json: {
      "Name": "Xin",
      "Age": 32,
      "Addresses": [
        {
          "Type": "private",
          "City": "Shanghai",
          "Country": "China"
        },
        {
          "Type": "work",
          "City": "Beijing",
          "Country": "China"
        }
      ]
    }
    

    Tips

    1. 在 web 应用中最好使用json.MarshalforHTML()函数,会对数据执行HTML转码。
    2. map 的 key 必须是字符串类型。
    3. Channel,复杂类型和函数类型不能被编码。
    4. 指针可以被编码,实际上是对指针指向的值进行编码.

    解码到数据结构

    如果事先知道 JSON 数据的结构,可以事先定义一个结构来存储反序列化后的结果。如,对这样一段 json:

    b := []byte(`{"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]}`)
    

    我们定义这样一个数据结构:

    type FamilyMember struct {
    	Name    string
    	Age     int
    	Parents []string
    }
    

    然后可以将其反序列化:

    var m FamilyMember
    err := json.Unmarshal(b, &m)
    

    完整的程序:

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    type FamilyMember struct {
        Name    string
        Age     int
        Parents []string
    }
    
    func main() {
        b := []byte(`{"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]}`)
        var m FamilyMember
        // json.Unmarshal 用于解码 json 串
        err := json.Unmarshal(b, &m)
        if err == nil {
            fmt.Printf("name: %s
    Age: %d
    Parents: %v
    ", m.Name, m.Age, m.Parents)
        }
    }
    

    执行后输出:

    name: Wednesday
    Age: 6
    Parents: [Gomez Morticia]
    

    解码任意数据

    json 包使用 map[string]interface{} 和 []interface{} 储存任意的 JSON 对象和数组。同样是上面的 JSON 串:

    b := []byte(`{"Name": "Wednesday", "Age": 6, "Parents": ["Gomez", "Morticia"]}`)
    

    用下面的代码进行解码:

    var f interface{}
    err := json.Unmarshal(b, &f)
    

    会得到:

    map[string]interface{} {
        "Name": "Wednesday",
        "Age":  6,
        "Parents": []interface{} {
            "Gomez",
            "Morticia",
        },
    }
    

    对于这类的数据,可以使用switch来判断节点的类型来遍历数据。



    动态更改JSON Sprintf

    Golang中使用 fmt.Sprintf 格式化 `JSON Data %s %d %f` 动态变更JSON数据.

    其等效作用是, 采用struct转换, 更改结构体数据, 然后Marshal成JSON, 用于 http处理

    JSON数据, 其中 %s 是占位符: 
    1. const jsonRes = `
    2. {
    3. "server": {
    4. "name": "ty",
    5. "imageRef": "f9436296-854f-4fe2-939d-eb667b245b78",
    6. "fmtSprintf": "%s",
    7. "max_count": 1,
    8. "min_count": 1,
    9. "networks": [
    10. {
    11. "uuid": "1e16b87f-ef66-4f0d-ba3d-d93234159076"
    12. }
    13. ],
    14. "metadata": {
    15. "ENV1": "100",
    16. "ENV2": "1000",
    17. "entrypoint": "/bin/sh -c 'while true; do echo hello world; sleep 99999d; done'"
    18. }
    19. }
    20. }
    21. `

    使用 fmt.Sprintf  格式化内容, 注意Sprintf返回格式化结果, 需要赋值给变量使用:
    1. func fmtSprintfJson() error {
    2. //var datm map[string]interface{}
    3. var datm interface{}
    4. const jsonRes = `
    5. {
    6. "server": {
    7. "name": "ty",
    8. "imageRef": "f9436296-854f-4fe2-939d-eb667b245b78",
    9. "fmtSprintf": "%s",
    10. "max_count": 1,
    11. "min_count": 1,
    12. "networks": [
    13. {
    14. "uuid": "1e16b87f-ef66-4f0d-ba3d-d93234159076"
    15. }
    16. ],
    17. "metadata": {
    18. "ENV1": "100",
    19. "ENV2": "1000",
    20. "entrypoint": "/bin/sh -c 'while true; do echo hello world; sleep 99999d; done'"
    21. }
    22. }
    23. }
    24. `
    25. jsonResSprintf := fmt.Sprintf(jsonRes, "test")
    26. fmt.Println("jsonResSprintf = ", jsonResSprintf)
    27. err := json.Unmarshal([]byte(jsonResSprintf), &datm)
    28. if err != nil {
    29. fmt.Println("json.Unmarshal([]byte(json), createRequests) , Error : ", err)
    30. return err
    31. }
    32. fmt.Println("struct request = ", datm)
    33. return nil
    34. }



    和 stream 中 JSON 打交道

    上面所有的 JSON 数据来源都是预先定义的 []byte 缓存,在很多时候,如果能读取/写入其他地方的数据就好了。encoding/json 库中有两个专门处理这个事情的结构:Decoder和 Encoder

    // Decoder 从 r io.Reader 中读取数据,`Decode(v interface{})` 方法把数据转换成对应的数据结构
    func NewDecoder(r io.Reader) *Decoder
    
    // Encoder 的 `Encode(v interface{})` 把数据结构转换成对应的 JSON 数据,然后写入到 w io.Writer 中
    func NewEncoder(w io.Writer) *Encoder
    

    下面的例子就是从标准输入流中读取数据,解析成数据结构,删除所有键不是 Name 的字段,然后再 encode 成 JSON 数据,打印到标准输出。

    package main
    
    import (
        "encoding/json"
        "log"
        "os"
    )
    
    func main() {
        dec := json.NewDecoder(os.Stdin)
        enc := json.NewEncoder(os.Stdout)
        for {
            var v map[string]interface{}
            if err := dec.Decode(&v); err != nil {
                log.Println(err)
                return
            }
            for k := range v {
                if k != "Name" {
                    delete(v, k)
                }
            }
            if err := enc.Encode(&v); err != nil {
                log.Println(err)
            }
        }
    }


    更多控制:Tag  

    在定义 struct 字段的时候,可以在字段后面添加 tag,来控制 encode/decode 的过程:是否要 decode/encode 某个字段,JSON 中的字段名称是什么。

    可以选择的控制字段有三种:

    • -:不要解析这个字段
    • omitempty:当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,slice,string
    • FieldName:当解析 json 的时候,使用这个名字

    举例来说吧:

    // 解析的时候忽略该字段。默认情况下会解析这个字段,因为它是大写字母开头的
    Field int   `json:"-"`
    
    // 解析(encode/decode) 的时候,使用 `other_name`,而不是 `Field`
    Field int   `json:"other_name"`
    
    // 解析的时候使用 `other_name`,如果struct 中这个值为空,就忽略它
    Field int   `json:"other_name,omitempty"`


    延迟解析:json.RawMessage  

    在解析的时候,还可以把某部分先保留为 JSON 数据不要解析,等到后面得到更多信息的时候再去解析。继续拿 User 举例,比如我们要添加认证的信息,认证可以是用户名和密码,也可以是 token 认证。

    type BasicAuth struct {
        Email string
        Password string
    }
    
    type TokenAuth struct {
        Token string
    }
    
    type User struct {
        Name string
        IsAdmin bool
        Followers uint
        Auth json.RawMessage
    }
    

    我们在定义 User 结构体的时候,把认证字段的类型定义为 json.RawMessage,这样解析 JSON 数据的时候,对应的字段会先不急着转换成 Go 数据结构。然后我们可以自己去再次调用 Unmarshal 去读取里面的值:

    1. data := []byte(`{"Name":"cizixs","IsAdmin":true,"Followers":36}`)
    2. err := json.Unmarshal(data, &basicAuth)
    3. if basicAuth.Email != "" {
    4. // 这是用户名/密码认证方式,在这里继续做一些处理
    5. } else {
    6. json.Unmarshal(data, &tokenAuth)
    7. if tokenAuth.Token != "" {
    8. // 这是 token 认证方法
    9. }
    10. }

    
    


    自定义解析方法  

    如果希望自己控制怎么解析成 JSON,或者把 JSON 解析成自定义的类型,只需要实现对应的接口(interface)。encoding/json 提供了两个接口:Marshaler 和 Unmarshaler

    // Marshaler 接口定义了怎么把某个类型 encode 成 JSON 数据
    type Marshaler interface {
            MarshalJSON() ([]byte, error)
    }
    
    // Unmarshaler 接口定义了怎么把 JSON 数据 decode 成特定的类型数据。如果后续还要使用 JSON 数据,必须把数据拷贝一份
    type Unmarshaler interface {
            UnmarshalJSON([]byte) error
    }
    

    标准库 time.Time 就实现了这两个接口。另外一个简单的例子(这个例子来自于参考资料中 Go and JSON 文章):

    type Month struct {
        MonthNumber int
        YearNumber int
    }
    
    func (m Month) MarshalJSON() ([]byte, error){
        return []byte(fmt.Sprintf("%d/%d", m.MonthNumber, m.YearNumber)), nil
    }
    
    func (m *Month) UnmarshalJSON(value []byte) error {
        parts := strings.Split(string(value), "/")
        m.MonthNumber = strconv.ParseInt(parts[0], 10, 32)
        m.YearNumber = strconv.ParseInt(parts[1], 10, 32)
    
        return nil
    }













  • 相关阅读:
    Android vector标签 PathData 画图超详解
    Echarts 出现不明竖线解决方案
    关于华为手机Log.d打印不出来log的问题
    Java源码解读(一)——HashMap
    算法小题
    Python简单小程序练习
    TCP/IP协议栈(三)——linux 向下的报文处理
    TCP协议(二)——TIME_WAIT状态
    Linux文件系统inode、block解释权限(三)
    Linux 基本权限(一)
  • 原文地址:https://www.cnblogs.com/xiangwengao/p/6297256.html
Copyright © 2011-2022 走看看