Go語言使用匿名結構體解析JSON資料

2020-07-16 10:05:18
JavaScript 物件表示法(JSON)是一種用於傳送和接收結構化資訊的標準協定。在類似的協定中,JSON 並不是唯一的一個標準協定。 XML、ASN.1 和 Google 的 Protocol Buffers 都是類似的協定,並且有各自的特色,但是由於簡潔性、可讀性和流行程度等原因,JSON 是應用最廣泛的一個。

Go語言對於這些標準格式的編碼和解碼都有良好的支援,由標準庫中的 encoding/json、encoding/xml、encoding/asn1 等包提供支援,並且這類包都有著相似的 API 介面。

基本的 JSON 型別有數位(十進位制或科學記數法)、布林值(true 或 false)、字串,其中字串是以雙引號包含的 Unicode 字元序列,支援和Go語言類似的反斜槓跳脫特性,不過 JSON 使用的是 Uhhhh 跳脫數位來表示一個 UTF-16 編碼,而不是Go語言的 rune 型別。

手機擁有螢幕、電池、指紋識別等資訊,將這些資訊填充為 JSON 格式的資料。如果需要選擇性地分離 JSON 中的資料則較為麻煩。Go語言中的匿名結構體可以方便地完成這個操作。

首先給出完整的程式碼,然後再講解每個部分。
package main

import (
    "encoding/json"
    "fmt"
)

// 定義手機螢幕
type Screen struct {
    Size       float32 // 螢幕尺寸
    ResX, ResY int     // 螢幕水平和垂直解析度
}

// 定義電池
type Battery struct {
    Capacity int // 容量
}

// 生成json資料
func genJsonData() []byte {
    // 完整資料結構
    raw := &struct {
        Screen
        Battery
        HasTouchID bool // 序列化時新增的欄位:是否有指紋識別
    }{
        // 螢幕引數
        Screen: Screen{
            Size: 5.5,
            ResX: 1920,
            ResY: 1080,
        },

        // 電池引數
        Battery: Battery{
            2910,
        },

        // 是否有指紋識別
        HasTouchID: true,
    }

    // 將資料序列化為json
    jsonData, _ := json.Marshal(raw)

    return jsonData
}

func main() {

    // 生成一段json資料
    jsonData := genJsonData()

    fmt.Println(string(jsonData))

    // 只需要螢幕和指紋識別資訊的結構和範例
    screenAndTouch := struct {
        Screen
        HasTouchID bool
    }{}

    // 反序列化到screenAndTouch
    json.Unmarshal(jsonData, &screenAndTouch)

    // 輸出screenAndTouch的詳細結構
    fmt.Printf("%+vn", screenAndTouch)

    // 只需要電池和指紋識別資訊的結構和範例
    batteryAndTouch := struct {
        Battery
        HasTouchID bool
    }{}

    // 反序列化到batteryAndTouch
    json.Unmarshal(jsonData, &batteryAndTouch)

    // 輸出screenAndTouch的詳細結構
    fmt.Printf("%+vn", batteryAndTouch)
}

定義資料結構

首先,定義手機的各種資料結構體,如螢幕和電池,參考如下程式碼:
// 定義手機螢幕
type Screen struct {
    Size       float32  // 螢幕尺寸
    ResX, ResY int      // 螢幕水平和垂直解析度
}

// 定義電池
type Battery struct {
    Capacity int  // 容量
}
上面程式碼定義了螢幕結構體和電池結構體,它們分別描述螢幕和電池的各種細節引數。

準備 JSON 資料

準備手機資料結構,填充資料,將資料序列化為 JSON 格式的位元組陣列,程式碼如下:
// 生成JSON資料
func genJsonData() []byte {
    // 完整資料結構
    raw := &struct {
        Screen
        Battery
        HasTouchID bool  // 序列化時新增的欄位:是否有指紋識別
    }{
            // 螢幕引數
        Screen: Screen{
            Size: 5.5,
            ResX: 1920,
            ResY: 1080,
        },

        // 電池引數
        Battery: Battery{
            2910,
        },

        // 是否有指紋識別
        HasTouchID: true,
    }

    // 將資料序列化為JSON
    jsonData, _ := json.Marshal(raw)

    return jsonData
}
程式碼說明如下:
  • 第 4 行定義了一個匿名結構體。這個結構體內嵌了 Screen 和 Battery 結構體,同時臨時加入了 HasTouchID 欄位。
  • 第 10 行,為剛宣告的匿名結構體填充螢幕資料。
  • 第 17 行,填充電池資料。
  • 第 22 行,填充指紋識別欄位。
  • 第 26 行,使用 json.Marshal 進行 JSON 序列化,將 raw 變數序列化為 []byte 格式的 JSON 資料。

分離JSON資料

呼叫 genJsonData 獲得 JSON 資料,將需要的欄位填充到匿名結構體範例中,通過 json.Unmarshal 反序列化 JSON 資料達成分離 JSON 資料效果。程式碼如下:
func main() {

    // 生成一段JSON資料
    jsonData := genJsonData()

    fmt.Println(string(jsonData))

    // 只需要螢幕和指紋識別資訊的結構和範例
    screenAndTouch := struct {
            Screen
            HasTouchID bool
    }{}

    // 反序列化到screenAndTouch中
    json.Unmarshal(jsonData, &screenAndTouch)

    // 輸出screenAndTouch的詳細結構
    fmt.Printf("%+vn", screenAndTouch)

    // 只需要電池和指紋識別資訊的結構和範例
    batteryAndTouch := struct {
            Battery
            HasTouchID bool
    }{}

    // 反序列化到batteryAndTouch
    json.Unmarshal(jsonData, &batteryAndTouch)

    // 輸出screenAndTouch的詳細結構
    fmt.Printf("%+vn", batteryAndTouch)
}
程式碼說明如下:
  • 第 4 行,呼叫 genJsonData() 函數,獲得 []byte 型別的 JSON 資料。
  • 第 6 行,將 jsonData 的 []byte 型別的 JSON 資料轉換為字串格式並列印輸出。
  • 第 9 行,構造匿名結構體,填充 Screen 結構和 HasTouchID 欄位,第 12 行中的 {} 表示將結構體範例化。
  • 第 15 行,呼叫 json.Unmarshal,輸入完整的 JSON 資料(jsonData),將資料按第 9 行定義的結構體格式序列化到 screenAndTouch 中。
  • 第 18 行,列印輸出 screenAndTouch 中的詳細資料資訊。
  • 第 21 行,構造匿名結構體,填充 Battery 結構和 HasTouchID 欄位。
  • 第 27 行,呼叫 json.Unmarshal,輸入完整的 JSON 資料(jsonData),將資料按第 21 行定義的結構體格式序列化到 batteryAndTouch 中。
  • 第 30 行,列印輸出 batteryAndTouch 的詳細資料資訊。