最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang?struct?json?tag的使用以及深入講解

 更新時間:2022年02月08日 09:55:14   作者:yuchenfw  
這篇文章主要給大家介紹了關于golang?struct?json?tag的使用以及深入講解,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

一、sturct json tag的使用

1.tag格式說明

struct json tag主要在struct與json數(shù)據(jù)轉(zhuǎn)換的過程(Marshal/Unmarshal)中使用。

json的tag格式如下:

Key type  `json:"name,opt1,opt2,opts..."`

說明:

  • 變量必須是可導出的(Key首字母必須大寫),否則會被忽略處理。
  • 沒有json tag或者tag中name省略(但不能少了","),默認使用字段名。
  • name要注意命名的有效性。
  • opt1、opt2等項為可選項,必須使用有限的幾個限定的opt的一個或組合,如"omitempty"、"string",使用非限定的opt會發(fā)生錯誤。

2.具體使用格式說明

我們先介紹下源碼文檔中提供的幾種使用方式:

因Marshal與Unmarshal是相反的過程,兩者規(guī)則是一致的,以下介紹中僅說明了Marshal時的處理。

(1)不指定tag

Field int // “Filed”:0

不指定tag,默認使用變量名稱。轉(zhuǎn)換為json時,key為Filed。

(2)直接忽略

Field int json:"-" //注意:必須為"-",不能帶有opts

轉(zhuǎn)換時不處理。

(3)指定key名

Field int json:"myName" // “myName”:0

轉(zhuǎn)換為json時,key為myName

(4)"omitempty"零值忽略

Field int json:",omitempty"

轉(zhuǎn)換為json時,值為零值則忽略,否則key為myName

(5)指定key且零值忽略

Field int json:"myName,omitempty"

轉(zhuǎn)換為json時,值為零值則忽略,否則key為myName

(6)指定key為"-"

Field int json:"-," // “-”:0

此項與忽略的區(qū)別在于多了個”,“。

(7)“string” opt

以上提到的用法都是常見的,這個比較特殊。

"string"僅適用于字符串、浮點、整數(shù)或布爾類型,表示的意思是:將字段的值轉(zhuǎn)換為字符串;解析時,則是將字符串解析為指定的類型。主要用于與javascript通信時數(shù)據(jù)的轉(zhuǎn)換。

注意:

僅且僅有"string",沒有int、number之類的opt。即帶"string" opt的字段,編碼時僅能將字符串、浮點、整數(shù)或布爾類型轉(zhuǎn)換為string類型,反之則不然;解碼時可以將string轉(zhuǎn)換為其他類型,反之不然。因為"string"有限制。

Int64String int64 json:",string" // “Int64String”:“0”

“string” opt的使用可以在Marshal/Unmarshal時自動進行數(shù)據(jù)類型的轉(zhuǎn)換,減少了手動數(shù)據(jù)轉(zhuǎn)換的麻煩,但是一定要注意使用的范圍,對不滿足的類型使用,是會報錯的。

猜下對string使用"string" opt的結(jié)果會是如何呢?

Int64String string json:",string"

我們在了解源碼后解答。

二、源碼角度的設計處理過程

一切的使用方式肯定在設計時就已限定,我們現(xiàn)在看看源碼中的處理過程。

在看實現(xiàn)的過程中,可以思考下使用的方式對不對,還有要注意的地方嗎?

對某些地方非常好的實現(xiàn)思路,我們也可以借鑒下,對以后的編程學習大有裨益。

此處為了簡潔,具體調(diào)用過程略過不講,直接查看核心代碼部分,有興趣的話,可以查看下完整過程。

1.typeFields

在typeFields中詳細的對上面提到的各種用法的tag做了處理,處理后的數(shù)據(jù)存入fileds,最后在進行編碼。

// typeFields returns a list of fields that JSON should recognize for the given type.
// The algorithm is breadth-first search over the set of structs to include - the top struct
// and then any reachable anonymous structs.
func typeFields(t reflect.Type) structFields {
    // Anonymous fields to explore at the current level and the next.
    current := []field{}
    next := []field{{typ: t}}

    // Count of queued names for current level and the next.
    var count, nextCount map[reflect.Type]int

    // Types already visited at an earlier level.
    visited := map[reflect.Type]bool{}

    // Fields found.
    var fields []field

    // Buffer to run HTMLEscape on field names.
    var nameEscBuf bytes.Buffer

    for len(next) > 0 {
        current, next = next, current[:0]
        count, nextCount = nextCount, map[reflect.Type]int{}

        for _, f := range current {
            if visited[f.typ] {//已處理的過類型跳過
                continue
            }
            visited[f.typ] = true

            // Scan f.typ for fields to include.
            for i := 0; i < f.typ.NumField(); i++ {
                sf := f.typ.Field(i)
                isUnexported := sf.PkgPath != ""
                if sf.Anonymous {//內(nèi)嵌類型的處理
                    t := sf.Type
                    if t.Kind() == reflect.Ptr {
                        t = t.Elem()
                    }
                    if isUnexported && t.Kind() != reflect.Struct {
                        // Ignore embedded fields of unexported non-struct types.
                        continue//非struct結(jié)構(gòu)的不能導出的key直接跳過
                    }
                    // Do not ignore embedded fields of unexported struct types
                    // since they may have exported fields.
                } else if isUnexported {
                    // Ignore unexported non-embedded fields.
                    continue//不能導出的key直接跳過
                }
                tag := sf.Tag.Get("json")
                if tag == "-" {
                    continue//tag為"-"直接跳過
                }
                name, opts := parseTag(tag)
                if !isValidTag(name) {
                    name = ""http://包含特殊字符的無效name
                }
                index := make([]int, len(f.index)+1)
                copy(index, f.index)
                index[len(f.index)] = i

                ft := sf.Type
                if ft.Name() == "" && ft.Kind() == reflect.Ptr {
                    // Follow pointer.
                    ft = ft.Elem()
                }

                // Only strings, floats, integers, and booleans can be quoted.
                quoted := false
                if opts.Contains("string") {//此處為"string" opt的特殊處理,支持的類型如下:
                    switch ft.Kind() {
                    case reflect.Bool,
                        reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
                        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
                        reflect.Float32, reflect.Float64,
                        reflect.String:
                        quoted = true
                    }
                }

                // Record found field and index sequence.
                if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
                    tagged := name != ""
                    if name == "" {
                        name = sf.Name//未指定或者指定name無效的使用原field的name
                    }
                    field := field{
                        name:      name,
                        tag:       tagged,
                        index:     index,
                        typ:       ft,
                        omitEmpty: opts.Contains("omitempty"),//omitempty確認
                        quoted:    quoted,//是否支持"string" opt
                    }
                    field.nameBytes = []byte(field.name)
                    field.equalFold = foldFunc(field.nameBytes)

                    // Build nameEscHTML and nameNonEsc ahead of time.
                    //兩種格式的構(gòu)建
                    nameEscBuf.Reset()
                    nameEscBuf.WriteString(`"`)
                    HTMLEscape(&nameEscBuf, field.nameBytes)
                    nameEscBuf.WriteString(`":`)
                    field.nameEscHTML = nameEscBuf.String()
                    field.nameNonEsc = `"` + field.name + `":`

                    fields = append(fields, field)//存入fields
                    if count[f.typ] > 1 {
                        // If there were multiple instances, add a second,
                        // so that the annihilation code will see a duplicate.
                        // It only cares about the distinction between 1 or 2,
                        // so don't bother generating any more copies.
                        fields = append(fields, fields[len(fields)-1])
                    }
                    continue
                }

                // Record new anonymous struct to explore in next round.
                nextCount[ft]++
                if nextCount[ft] == 1 {
                    next = append(next, field{name: ft.Name(), index: index, typ: ft})
                }
            }
        }
    }

    ...

    for i := range fields {
        f := &fields[i]
        f.encoder = typeEncoder(typeByIndex(t, f.index))//設置fields的encoder
    }
    nameIndex := make(map[string]int, len(fields))
    for i, field := range fields {
        nameIndex[field.name] = i
    }
    return structFields{fields, nameIndex}
}

2.encode

func newStructEncoder(t reflect.Type) encoderFunc {
    se := structEncoder{fields: cachedTypeFields(t)}
    return se.encode
}

func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
    next := byte('{')
FieldLoop:
    for i := range se.fields.list {
        f := &se.fields.list[i]

        // Find the nested struct field by following f.index.
        fv := v
        for _, i := range f.index {
            if fv.Kind() == reflect.Ptr {
                if fv.IsNil() {
                    continue FieldLoop
                }
                fv = fv.Elem()
            }
            fv = fv.Field(i)
        }

        if f.omitEmpty && isEmptyValue(fv) {//"omitempty"的忽略處理,需要值為零值
            continue
        }
        e.WriteByte(next)
        next = ','
        if opts.escapeHTML {
            e.WriteString(f.nameEscHTML)
        } else {
            e.WriteString(f.nameNonEsc)
        }
        opts.quoted = f.quoted
        f.encoder(e, fv, opts)//根據(jù)具體類型的編碼處理
    }
    if next == '{' {
        e.WriteString("{}")
    } else {
        e.WriteByte('}')
    }
}

以下以int類型intEncoder為例:

func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
    b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
    if opts.quoted {//帶有"string" opt添加引號
        e.WriteByte('"')
    }
    e.Write(b)
    if opts.quoted {
        e.WriteByte('"')
    }
}

對于數(shù)字類型,如果帶有**“string”**則在寫入正式值前后添加引號。

對于字符串類型,如果帶有**“string”**,原string值再編碼時會添加引號,再對結(jié)果添加引號,則格式異常,因此需要先對原值進行編碼。

func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
    if v.Type() == numberType {
        numStr := v.String()
        // In Go1.5 the empty string encodes to "0", while this is not a valid number literal
        // we keep compatibility so check validity after this.
        if numStr == "" {
            numStr = "0" // Number's zero-val
        }
        if !isValidNumber(numStr) {
            e.error(fmt.Errorf("json: invalid number literal %q", numStr))
        }
        e.WriteString(numStr)
        return
    }
    if opts.quoted {
        sb, err := Marshal(v.String())//注意此處處理
        if err != nil {
            e.error(err)
        }
        e.string(string(sb), opts.escapeHTML)
    } else {
        e.string(v.String(), opts.escapeHTML)
    }
}

func (e *encodeState) string(s string, escapeHTML bool) {
    e.WriteByte('"')//添加引號
    start := 0
    for i := 0; i < len(s); {
        if b := s[i]; b < utf8.RuneSelf {//字符串中存在特殊的字符時的轉(zhuǎn)義處理
            if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
                i++
                continue
            }
            if start < i {
                e.WriteString(s[start:i])
            }
            e.WriteByte('\\')
            switch b {
            case '\\', '"':
                e.WriteByte(b)
            case '\n':
                e.WriteByte('n')
            case '\r':
                e.WriteByte('r')
            case '\t':
                e.WriteByte('t')
            default:
                // This encodes bytes < 0x20 except for \t, \n and \r.
                // If escapeHTML is set, it also escapes <, >, and &
                // because they can lead to security holes when
                // user-controlled strings are rendered into JSON
                // and served to some browsers.
                e.WriteString(`u00`)
                e.WriteByte(hex[b>>4])
                e.WriteByte(hex[b&0xF])
            }
            i++
            start = i
            continue
        }
        c, size := utf8.DecodeRuneInString(s[i:])
        if c == utf8.RuneError && size == 1 {
            if start < i {
                e.WriteString(s[start:i])
            }
            e.WriteString(`\ufffd`)
            i += size
            start = i
            continue
        }
        // U+2028 is LINE SEPARATOR.
        // U+2029 is PARAGRAPH SEPARATOR.
        // They are both technically valid characters in JSON strings,
        // but don't work in JSONP, which has to be evaluated as JavaScript,
        // and can lead to security holes there. It is valid JSON to
        // escape them, so we do so unconditionally.
        // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
        if c == '\u2028' || c == '\u2029' {
            if start < i {
                e.WriteString(s[start:i])
            }
            e.WriteString(`\u202`)
            e.WriteByte(hex[c&0xF])
            i += size
            start = i
            continue
        }
        i += size
    }
    if start < len(s) {
        e.WriteString(s[start:])
    }
    e.WriteByte('"')
}

在了解完源碼的處理過程后,我們對之前提到的問題做個解答。對string類型的字段添加"string" opt,得到的是:

Int64String string json:",string" // “Int64String”: "“1234"”

三、總結(jié)

本文主要從源碼的角度說明struct json tag的為什么這么使用,以及使用時需要注意的地方。最后重復下重要的幾點:

  • 字段必須可導出,tag才有意義
  • 忽略必須使用json:"-",不得帶有opts,否則key將會變成"-"
  • "string" opt僅適用于字符串、浮點、整數(shù)及布爾類型,意思是可以將這些類型的數(shù)據(jù)Marshal為string類型,或者將string類型的數(shù)據(jù)Unmarshal為這些類型。

請勿濫用,尤其是對已經(jīng)是string類型的數(shù)據(jù)使用。

到此這篇關于golang struct json tag使用的文章就介紹到這了,更多相關golang struct json tag的使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang利用compress/flate包來壓縮和解壓數(shù)據(jù)

    Golang利用compress/flate包來壓縮和解壓數(shù)據(jù)

    在處理需要高效存儲和快速傳輸?shù)臄?shù)據(jù)時,數(shù)據(jù)壓縮成為了一項不可或缺的技術,Go語言的compress/flate包為我們提供了對DEFLATE壓縮格式的原生支持,本文將深入探討compress/flate包的使用方法,揭示如何利用它來壓縮和解壓數(shù)據(jù),并提供實際的代碼示例,需要的朋友可以參考下
    2024-08-08
  • Go語言編譯原理之源碼調(diào)試

    Go語言編譯原理之源碼調(diào)試

    這篇文章主要為大家介紹了Go語言編譯原理之源碼調(diào)試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • 利用Go語言實現(xiàn)簡單Ping過程的方法

    利用Go語言實現(xiàn)簡單Ping過程的方法

    相信利用各種語言實現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡上利用Golang實現(xiàn)Ping已經(jīng)有比較詳細的代碼示例,但大多是僅僅是實現(xiàn)了Request過程,而對Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個ICMP,更重要的是如何接收并進行統(tǒng)計。
    2016-09-09
  • golang通過http訪問外部網(wǎng)址的操作方法

    golang通過http訪問外部網(wǎng)址的操作方法

    這篇文章主要介紹了golang通過http訪問外部網(wǎng)址的操作方法,分為?get方式訪問外部的接口,Post方式請求外部接口,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2024-02-02
  • Go語言中的iota關鍵字的使用

    Go語言中的iota關鍵字的使用

    這篇文章主要為大家詳細介紹了Go語言中的iota關鍵字的相關使用,文中的示例代碼講解詳細,對我們深入了解Go語言有一定的幫助,需要的可以參考下
    2023-08-08
  • 簡單聊聊Golang中Flag包的具體使用

    簡單聊聊Golang中Flag包的具體使用

    flag 包是 Go 語言標準庫中的一個包,用于解析命令行參數(shù),這篇文章主要來和大家簡單聊聊Golang中Flag包的具體使用,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-05-05
  • 詳解Golang中gomock的使用場景和方法

    詳解Golang中gomock的使用場景和方法

    gomock是Go編程語言的模擬框架, 它與Go的內(nèi)置測試包很好地集成在一起,但也可以在其他上下文中使用,本文主要介紹了gomock的使用場景和方法,感興趣的可以了解下
    2024-10-10
  • Go無緩沖通道(同步通道)的實現(xiàn)

    Go無緩沖通道(同步通道)的實現(xiàn)

    本文主要介紹了Go無緩沖通道(同步通道)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-02-02
  • Golang信號處理及如何實現(xiàn)進程的優(yōu)雅退出詳解

    Golang信號處理及如何實現(xiàn)進程的優(yōu)雅退出詳解

    這篇文章主要給大家介紹了關于Golang信號處理及如何實現(xiàn)進程的優(yōu)雅退出的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-03-03
  • 使用Golang的Context管理上下文的方法

    使用Golang的Context管理上下文的方法

    這篇文章主要介紹了使用Golang的Context管理上下文的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09

最新評論

炎陵县| 时尚| 屏山县| 绿春县| 交口县| 万宁市| 威宁| 诸暨市| 屏南县| 长丰县| 济宁市| 于田县| 吴旗县| 巧家县| 武义县| 甘南县| 托克逊县| 肥西县| 酒泉市| 隆昌县| 威海市| 巩留县| 溧水县| 定日县| 惠来县| 景宁| 桂林市| 澎湖县| 长葛市| 绥阳县| 珠海市| 巨野县| 清徐县| 绥宁县| 宁南县| 利辛县| 伊宁县| 正镶白旗| 江北区| 乾安县| 玉山县|