package
0.0.0-20240927143213-b11bb4fab650
Repository: https://github.com/jocelynh1110/go-practice.git
Documentation: pkg.go.dev

# README

11-5 處理內容未知的 JSON 資料

11-5-1 將 JSON 格式解碼成 map

有時候無法預知 JSON 的實際結果,比如某個網站 API 會產生動態的 JSON 回應,在不同情況下會有不同的鍵與值,或 API 本身規格會常常更新,導致傳回結果頻頻變動。
json.Unmarshal() 不只能將 JSON 解碼,也能使用 map 來儲存資料。

可將 map 定義成下面:

map[string]interface{}      

[string] 是 JSON 鍵 interface{} 是 JSON 值
Unmarshal() 會將 JSON 資料中的任何鍵轉成 map 鍵,並將值配對給對應鍵。
JSON 鍵一定是字串,值可能是不同型別,所以用空介面接收。如此以來不管 JSON 資料中有什麼東西都可以放進 map。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	jsonData := []byte(`{"checkNum":123,"amount":200,"category":["gift","clothing"]}`)

	// 定義 map
	var v map[string]interface{} // JSON 鍵一定是字串,值有可能是不同型別,故用空介面接收

	// 將 JSON 資料解碼到 map
	json.Unmarshal(jsonData, &v)

	// 印出 map 內容和走訪它
	fmt.Println(v)
	for key, value := range v {
		fmt.Println(key, "=", value)
	}
}

顯示結果:

map[amount:200 category:[gift clothing] checkNum:123]
amount = 200
category = [gift clothing]
checkNum = 123

以上並未初始化以上並未初始化 map 變數 v,因為 Unmarshal() 會自己初始化。若 v 在傳入 Unmarshal() 前就有內容,那 Unmarshal() 會在 v 中新增其他鍵與值。

練習、分析選課 JSON 資料內容:

package main

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

func main() {
	jsonData := []byte(`
	{
		"id":2,
	"lname":"John",
	"fname":"Josh",
	"IsEnrolled":true,
	"grades":[100,76,93,50],
	"class":
		{
			"coursename":"History",
			"coursenum":101,
			"coursehours":3
		}
	}
	`)

	if !json.Valid(jsonData) { // 先檢查資料是否符合 JSON 格式
		fmt.Println("JSON 格式不合法:", jsonData)
		os.Exit(1)
	}

	// 解碼 JSON 格式到 map
	var v map[string]interface{}
	if err := json.Unmarshal(jsonData, &v); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	// 走訪 map
	for key, value := range v {
		fmt.Printf("%s = %v (%s)\n", key, value, findTypeName(value))
	}
}

// 用型別斷言來檢查值的函式
func findTypeName(i interface{}) string {
	switch i.(type) { // 型別斷言
	case string:
		return "string"
	case int:
		return "int"
	case float64:
		return "float64"
	case bool:
		return "bool"
	default:
		return fmt.Sprintf("%T", i)
	}
}

顯示結果:

grades = [100 76 93 50] ([]interface {})
class = map[coursehours:3 coursename:History coursenum:101] (map[string]interface {})
id = 2 (float64)
lname = John (string)
fname = Josh (string)
IsEnrolled = true (bool)

即使不曉得 JSON 資料中的鍵與值如何組成。仍能用一個 map[string]interface{} 容器來儲存解碼結果。 當每個值都透過空介面儲存,事後就得做型別斷言或使用 fmt.Sprintf() 才能判斷值的型別。