golang json编码

数据的json编码主要涉及两个方法调用:

1.Marshal方法

该方法返回参数v的json编码

func Marshal(v interface{}) ([]byte, error) {
    e := newEncodeState()

    err := e.marshal(v, encOpts{escapeHTML: true})
    if err != nil {
        return nil, err
    }
    buf := append([]byte(nil), e.Bytes()...)

    e.Reset()
    encodeStatePool.Put(e)

    return buf, nil
}

该方法可以接受任意类型的参数,并将其转化为字节序列,如果遇到的值实现了Marshaler接口,并且不是一个空指针,Marshal方法就会调用其上的MarshalJSON方法来产生JSON对象。如果没有MarshalJSON方法,但是它实现了encoding.TextMarshaler,Marshal调用marshalText方法,并将结果编码为JSON字符串。否则,Marshal方法使用以下的依赖于类型的默认编码;

  • boolean值编码为JSON的boolean值
  • 浮点数,整型,Number类型的值编码为JSON的number值
  • 字符串类型编码为有效的UTF-8编码的JSON字符串值,使用rune类型替换无效的字节,尖括号<、>变为"\u003c","\u003e",以防止某些浏览器将JSON输出错误的解释为HTML."&"变成"\u0026"也是同样的原因。可以通过SetEscapeHTML(false)来禁止此转义。
  • 数组和切片类型编码为JSON数组,除了[]byte字节数组编码为bash64的字符串。nil的切片会编码为null的JSON值。
    var a []int = []int{1, 2, 3, 4, 6}
    sliceJSON, err := json.Marshal(a)
    fmt.Println(string(sliceJSON)) //[1,2,3,4,6]
  • 结构体编码为JSON对象。每一个导出的结构体字段变成JSON对象的成员,使用字段名作为JSON对象的一个key.除非字段以以下的方式被省略了:
    每个字段的编码可以通过结构体的字段标签来定制。标签可以给出字段的名称,也可以为空, 来使用默认的字段名。omitempty可选项指定字符如果为空值,在编码的时候,需要忽略,以下值可以定义为空值:false,0,nil指针,nil接口,空数组,空字符串,空字典,空切片。特别的,如果字段标签为"-"",字段将在编时被忽略,如果标签为"-,"",编码后,出现在JSON对象中时,字段名将为-。
  • "string"字符串标签标示字段将被编码为JSON字符串,该标签仅在字段类型为字符串,浮点数,整数,布尔类型时,使用。
  • 指针类型的值编码为指针指向的值,空指针将被编码为null值。
  • 接口类型的值将被编码为接口中包含的值,空的接口值将被编码为null值。
  • channel,complex,函数类型的值不能被编码,试图编码将报错。
  • 循环引用的数据Marshal将不处理它们,传入一个循环结构的数据到Marshal中将导致一个无线循环。
package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

type User struct {
    Name  string `json:"user_name"`
    Age   int    `json:"user_age"`
    Addr  string `json:"address,omitempty"`
    Email string `json:",omitempty"`
    *IphoneInfo
}

type IphoneInfo struct {
    Id     int64       `json:"iphone_id"`
    Name   string      `json:"iphone_name"`
    IpAddr interface{} `json:"iphone_ip_addr"`
    IphoneType
}

type IphoneType struct {
    IphoneNumber string `json:"iphone_number"`
    Is5GIphone   bool   `json:"is_5g"`
}

func main() {
    var user User = User{
        Name: "yechongqiu",
        Age:  18,
        Addr: "Nanjiing",
        // Email: "yechongqiu@sunning.com",
    }
    fmt.Println(reflect.ValueOf(user) == reflect.ValueOf(&user))
    fmt.Println(reflect.ValueOf(&user))
    jsonUser, err := json.Marshal(&user)
    if err != nil {
        return
    }
    fmt.Println(string(jsonUser))
    var a []int = []int{1, 2, 3, 4, 6}
    sliceJSON, err := json.Marshal(a)
    fmt.Println(string(sliceJSON))

    iphoneInfo := &IphoneInfo{
        Id:     100001,
        Name:   "iphone 8",
        IpAddr: "192.168.10.101",
    }
    user.IphoneInfo = iphoneInfo
    jsonUser2, err := json.Marshal(user)
    if err != nil {
        return
    }
    fmt.Println(string(jsonUser2))
}

Output:

false
&{yechongqiu 18 Nanjiing  <nil>}
{"user_name":"yechongqiu","user_age":18,"address":"Nanjiing"}
[1,2,3,4,6]
{"user_name":"yechongqiu","user_age":18,"address":"Nanjiing","iphone_id":100001,"iphone_name":"iphone 8","iphone_ip_addr":"192.168.10.101","iphone_number":"","is_5g":false}

Unmarshal

将JSON对象解码为结构体数据,方法的参数为:

  • 需要解码的JSON对象的字节序列
  • 将JOSN对象解码到的结构体,一般为指针。
func Unmarshal(data []byte, v interface{}) error {
    // Check for well-formedness.
    // Avoids filling out half a data structure
    // before discovering a JSON syntax error.
    var d decodeState
    err := checkValid(data, &d.scan)
    if err != nil {
        return err
    }

    d.init(data)
    return d.unmarshal(v)
}

如果第二个参数为空,或者不是一个指针,将返回一个错误。Unmarshal使用Marshal相反的编码。使用以下附加规则:

为了解码JSON到一个实现了Unmarshaler接口的值,Unmarshal方法调用值的UnmarshalJSON方法。包含输入为JSON null的情况。否则,如果值实现了encoding.TextUnmarshaler接口,Unmarshal方法将调用值的该方法。
为了解码一个JOSN到一个结构体,Unmarshal匹配输入类型的key到Marshal时使用的key.

解码JSON到一个接口值时,将如下的值保存到接口中去:

  • json boolean -> bool
  • json number -> float64
  • json string -> string
  • json arrays -> []interface{}
  • json objects -> map[string]interface{}
  • json null -> nil

为了将一个JSON数组解码到一个切片中,Unmarshal将重置切片长度为0,然后再将每个元素追加到切片中。特别的,将一个空的JSON数组解码到一个切片中,Unmarshal使用一个空的切片替换切片。

为了将一个JSON对象解码到map中,Unmarshal 首先建立一个map,如果map为nil,将重新分配一个,否则直接使用它,保留之前的键值对,然后将JSON对象的key-value对存到map中去。map的键类型必须是一个字符串,或者整数,或者实现了excoding.TextUnmarshaler接口的类型。

如果一个JSON值不适合给的目标值的类型。或者JSON数字值范围超过了目标类型的范围,Unmarshal将跳过它,尽可能的完成其他解码。如果没有遇到更加严重的错误,Unmarshal将返回一个描述早前错误的异常。

    err = json.Unmarshal(jsonUser2, &userFromJSON)
    if err != nil {
        return
    }
    fmt.Println(userFromJSON)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容