欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析golang如何處理json中的null

 更新時間:2023年09月19日 11:17:56   作者:uccs  
json?是一種常用的數據格式,在?go?使用?json?序列化和反序列化時比較方便的,但在使用過程中,會遇到一些問題,比如?null,所以下面我們就來看看golang如何處理json中的null吧

最近學習 go 發(fā)現發(fā)現處理 json 中的 null 時,會這么難受,需要專門寫一篇文章來講解一下

以下是正文

json 是一種常用的數據格式,在 go 使用 json 序列化和反序列化時比較方便的,但在使用過程中,會遇到一些問題,比如 null

由于 go 沒有聯合類型,當 json 中有個屬性為 null 時,就無法直接將 null 轉換成 nil 后賦值給某個具體的類型

比如下面這個例子:

Name 定一個的是 string 類型,但在 jsonname 的值為 null,直接轉換會報錯

type Tag struct {
  ID   int    `json:"id"`
  Name string `json:"name"`
}
tag := Tag{
  ID:   1,
  Name: nil,  // 這里會報錯
}

這種問題不光出現在 json 解析時,還會出現在數據庫讀寫時

比如在數據庫中,某個字段的值為 NULL,在讀取時,會被解析成 nil,但是 go 中的類型是不能直接賦值為 nil

所以在這兩種場景下該怎么解決呢?

一般有三種方法:

  • 使用指針
  • 自定義類型
  • 使用第三方庫

使用指針

go 的指針類型是可以賦值為 nil 的,所以我們使用指針解決這個問題

我們把上面例子中的 Name 定義為 string 的指針類型,如下代碼:

type Tag struct {
  ID   int    `json:"id"`
  Name *string `json:"name"`
}
name := "uccs" // 定義一個 string 類型的變量,因為不能把一個字面量直接賦值給指針類型
tag := Tag{
  ID:   1,
  Name: &name,  // 將 name 的地址賦值給 Name,使用 & 地址符
}

在使用時,需要先判斷一下 Name 是為 nil,如果不為 nil,則使用 * 取值符取出值

// Name 是指針類型,判斷是否為 nil 時不需要使用 * 取值符
if tag.Name != nil {
  // Name 是指針類型,取值時需要使用 * 取值符
  if *tag.Name == "uccs" {
    // ...
  }
}

注意事項

ORM 框架會實現一個 NullString 的類型,

當我們在定義 Model 時,如果某個字段可以為 NULL,則 ORM 框架會把它定義為 NullString 類型(下文講解)

給指針賦值時,不能直接使用字面量,需要先定義一個變量,然后將變量的地址賦值給指針

使用指針時需要注意,這里會比較繞

在判斷是否為 nil 時,不需要使用 * 取值符

在判斷是否為 uccs 時,需要使用 * 取值符

當遇到 panic: runtime error: invalid memory address or nil pointer dereference 錯誤時,說明指針為 nil

也就是說使用指針時,我們最需要注意的是:在指針上取值時,一定要注意它是不是為 nil

自定義類型

我們使用結構體定義一個類型:NullString,它有兩個屬性 StringValid

String 用來存儲字符串

Valid 用來標識 String 是否有值

  • 如果 Validtrue,則 String 有值
  • 如果 Validfalse,則 String 是空值 ""
type NullString struct {
  String string
  Valid  bool
}

當我們定義好類型后,需要考考慮兩個問題:

  • 如何解決 json 解析時 null 的問題
  • 如何向數據庫進行讀寫

go 有個特點,你自定義的類型有某些方法,那么在某些場景下,這些方法會被調用

比如,序列化時,會調用 MarshalJSON 方法,反序列化時,會調用 UnmarshalJSON 方法

你的自定義類型實現了這兩個方法,那么在序列化和反序列化時,這兩個方法就會被調用

數據庫讀寫是實現 ScanValue 方法

所以下面就從這兩塊講起:

序列化和反序列化

我們給 NullString 類型添加兩個方法 MarshalJSONUnmarshalJSON

// 序列化時
func (ns NullString) MarshalJSON() ([]byte, error) {
  // 如果 Valid 為 true,則返回 String 的 json 序列化結果
  if ns.Valid {
    return []byte(`"` + ns.String + `"`), nil
  }
  // 如果 Valid 為 false,則返回 null 序列化的結果
  return []byte("null"), nil
}
// 反序列化
func (ns *NullString) UnmarshalJSON(data []byte) error {
  // 如果 data 為 null,則 Valid 為 false
  // String 為空字符串
  if string(data) == "null" {
    ns.String, ns.Valid = "", false
    return nil
  }
  // 否則,將 data 反序列化到 String 中
  // 并將 Valid 設置為 true
  if err := json.Unmarshal(data, &ns.String); err != nil {
    return err
  }
  ns.Valid = true
  return nil
}

有了這兩個方法之后,我們就解決了 json 解析時 null 的問題

是什么時候會觸發(fā)這兩個方法呢?

json 內容解析填充 struct 的場景時會觸發(fā) UnmarshalJSON 的調用

  • 直接調用 json.Unmarshaljson 數據進行解析時
  • http.Request 讀取 json Body
  • 使用 encoding/jsonDecoder 進行解碼時
  • 對實現了 Unmarshaler 接口的對象調用 UnmarshalJSON 方法時

反過來,將 struct 內容序列化為 json 時會觸發(fā) json.Marshal 的調用

  • 直接調用 json.Marshal 對一個對象進行編碼
  • 使用 http.ResponseWriterWrite 方法響應 json 數據時
  • 使用 encoding/jsonEncoder 進行編碼時
  • 對實現了 Marshaler 接口的對象調用 MarshalJSON 方法時

序列化和反序列化問題解決了,那如何向數據庫進行讀寫呢?

數據庫讀寫

我們再給 NullString 添加兩個方法 ValueScan

  • Value 方法會在寫入數據庫時被調用
  • Scan 方法會在從數據庫讀取時被調用
// Scan 方法在 數據庫讀取時被調用
func (ns *NullString) Scan(value interface{}) error {
  // 如果 value 為 nil,則 Valid 為 false,String 為空字符串
  if value == nil {
    ns.String, ns.Valid = "", false
    return nil
  }
  // 否則,將 value 斷言為 string 類型,斷言成功 Valid 為 true,String 為 value
  ns.String, ns.Valid = value.(string)
  return nil
}
// Value 方法 在寫入數據庫時被調用
func (ns NullString) Value() (driver.Value, error) {
  // 如果 Valid 為 false,則返回 nil
  if !ns.Valid {
    return nil, nil
  }
  // 否則,返回 String
  return ns.String, nil
}

添加這兩個方法后,我們就可以向數據庫中寫入 null

是什么時候會觸發(fā)這兩個方法呢?

Scanner 接口的 Scan 方法會在以下情況被調用

ORM 框架如 GORM、database/sql 等查詢時,掃描結果到自定義模型

Valuer 接口的 Value 方法會在以下情況被調用

ORM 框架如 GORM、database/sql 構造寫入語句時,獲取自定義模型的值

使用

將上面 Tag 的解構體改為:

type Tag struct {
  ID   int        `json:"id"`
  Name NullString `json:"name"`
}

不過這里要注意的一點是,在給 Name 賦值時,需要使用 NullString 進行賦值,如果下所示:

tag := Tag{
  ID:   1,
  Name: NullString{String: "hello", Valid: true},
}

最后需要注意的是,go 中其他類型也要實現這樣的方法,比如 NullInt,NullBool 等,可以參照這個 guregu/null 這個庫

使用第三方庫

第三方庫 guregu/null 已經實現了上面的方法,我們可以直接使用

ORM 一般都實現了這些功能

需要注意的是有些 ORM 只實現了 ScannerValuer 接口,沒有實現 MarshalJSONUnmarshalJSON 接口

總結

  • 使用 string 只能滿足必填的情況
  • ORM 框架一般都實現了 ScannerValuer 接口,但是有些 ORM 沒有實現 MarshalJSONUnmarshalJSON 接口,需要自己實現,或者使用第三方庫
  • 使用指針時,如 *string,需要注意指針是否為 nil

到此這篇關于淺析golang如何處理json中的null的文章就介紹到這了,更多相關go處理json內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 基于Golang開發(fā)一個輕量級登錄庫/框架

    基于Golang開發(fā)一個輕量級登錄庫/框架

    幾乎每個項目都會有登錄,退出等用戶功能,而登錄又不單僅僅是登錄,我們要考慮很多東西。所以本文就來用Golang開發(fā)一個輕量級登錄庫/框架吧
    2023-05-05
  • golang開發(fā)中channel使用

    golang開發(fā)中channel使用

    channel[通道]是golang的一種重要特性,正是因為channel的存在才使得golang不同于其它語言。這篇文章主要介紹了golang開發(fā)中channel使用,需要的朋友可以參考下
    2020-09-09
  • 使用GORM將PostgreSQL集成到Go框架中

    使用GORM將PostgreSQL集成到Go框架中

    在go中集成postgresql需使用gorm orm,步驟如下:安裝go和postgresql,安裝 gorm:go get -u gorm.io/gorm,配置數據庫連接字符串,定義模型類,遷移數據庫架構,使用 gorm 進行增刪改查操作,本指南將介紹如何使用 GORM(一個廣受歡迎的 ORM),將PostgreSQL集成到你的Go應用中
    2024-08-08
  • Go語言中的自定義類型你了解嗎

    Go語言中的自定義類型你了解嗎

    自定義類型是 Go 語言中非常重要的概念之一,通過自定義類型,我們可以更好地封裝數據、組織代碼,提高程序的可讀性和可維護性。本文將從以下幾個方面介紹 Go 自定義類型的相關知識,感興趣的可以了解一下
    2023-04-04
  • golang實現RPC模塊的示例

    golang實現RPC模塊的示例

    本文詳細介紹了在Go語言中如何實現RPC模塊,包括RPC服務端和客戶端的構建及代碼實現,同時提到了使用JSON-RPC的方法,通過簡單的步驟,可以實現跨進程的遠程過程調用,感興趣的可以了解一下
    2024-09-09
  • go cron定時任務的基本使用講解

    go cron定時任務的基本使用講解

    這篇文章主要為大家介紹了gocron定時任務的基本使用講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • golang兩種調用rpc的方法

    golang兩種調用rpc的方法

    這篇文章主要介紹了golang兩種調用rpc的方法,結合實例形式分析了Go語言調用rpc的原理與實現方法,需要的朋友可以參考下
    2016-07-07
  • Go語言select語句用法示例

    Go語言select語句用法示例

    這篇文章主要為大家介紹了Go語言select語句用法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go項目中的GOPROXY設置

    Go項目中的GOPROXY設置

    GOPROXY是Go語言中用于指定模塊代理服務器的環(huán)境變量,設置GOPROXY可以通過操作系統環(huán)境變量、Go命令行參數或Go環(huán)境配置文件進行,感興趣的可以了解一下
    2024-09-09
  • golang實現命令行程序的使用幫助功能

    golang實現命令行程序的使用幫助功能

    這篇文章介紹了golang實現命令行程序使用幫助的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07

最新評論