您是否曾經需要在 Go 中修改非結構化 JSON 資料?也許您必須刪除所有列入黑名單的字段,將鍵從camelCase重命名為snake_case,或者將所有數字ID轉換為字串,因為JavaScript不喜歡int64? 如果您的解決方案是使用編碼/json 將所有內容解組到 map[string]any 中,然後將其編組回去...好吧,讓我們面對現實吧,這遠非高效!
如果您可以循環遍歷 JSON 數據,獲取每個項目的路徑,並即時決定如何處理它,會怎麼樣?
是的!我有一個好消息!借助 Go 1.23 中的新迭代器功能,有更好的方法來迭代和操作 JSON。來認識 ezpkg.io/iter.json — 在 Go 中使用 JSON 的強大而高效的伴侶。
假設我們有一個 alice.json 檔案:
{ "name": "Alice", "age": 24, "scores": [9, 10, 8], "address": { "city": "The Sun", "zip": 10101 } }
首先,讓我們使用 for range Parse() 迭代 JSON 文件,然後列印每個項目的路徑、鍵、令牌和層級。請參閱範例/01.iter。
package main import ( "fmt" "ezpkg.io/errorz" iterjson "ezpkg.io/iter.json" ) func main() { data := `{"name": "Alice", "age": 24, "scores": [9, 10, 8], "address": {"city": "The Sun", "zip": 10101}}` // ?Example: iterate over json fmt.Printf("| %12v | %10v | %10v |%v|\n", "PATH", "KEY", "TOKEN", "LVL") fmt.Println("| ------------ | ---------- | ---------- | - |") for item, err := range iterjson.Parse([]byte(data)) { errorz.MustZ(err) fmt.Printf("| %12v | %10v | %10v | %v |\n", item.GetPathString(), item.Key, item.Token, item.Level) } }
程式碼將輸出:
| PATH | KEY | TOKEN |LVL| | ------------ | ---------- | ---------- | - | | | | { | 0 | | name | "name" | "Alice" | 1 | | age | "age" | 24 | 1 | | scores | "scores" | [ | 1 | | scores.0 | | 9 | 2 | | scores.1 | | 10 | 2 | | scores.2 | | 8 | 2 | | scores | | ] | 1 | | address | "address" | { | 1 | | address.city | "city" | "The Sun" | 2 | | address.zip | "zip" | 10101 | 2 | | address | | } | 1 | | | | } | 0 |
使用Builder建立JSON資料。它接受可選的縮排參數。請參閱範例/02.builder。
b := iterjson.NewBuilder("", " ") // open an object b.Add("", iterjson.TokenObjectOpen) // add a few fields b.Add("name", "Alice") b.Add("age", 22) b.Add("email", "alice@example.com") b.Add("phone", "(+84) 123-456-789") // open an array b.Add("languages", iterjson.TokenArrayOpen) b.Add("", "English") b.Add("", "Vietnamese") b.Add("", iterjson.TokenArrayClose) // close the array // accept any type that can marshal to json b.Add("address", Address{ HouseNumber: 42, Street: "Ly Thuong Kiet", City: "Ha Noi", Country: "Vietnam", }) // accept []byte as raw json b.Add("pets", []byte(`[{"type":"cat","name":"Kitty","age":2},{"type":"dog","name":"Yummy","age":3}]`)) // close the object b.Add("", iterjson.TokenObjectClose) out := errorz.Must(b.Bytes()) fmt.Printf("\n--- build json ---\n%s\n", out)
這將輸出帶縮排的 JSON:
{ "name": "Alice", "age": 22, "email": "alice@example.com", "phone": "(+84) 123-456-789", "languages": [ "English", "Vietnamese" ], "address": {"house_number":42,"street":"Ly Thuong Kiet","city":"Ha Noi","country":"Vietnam"}, "pets": [ { "type": "cat", "name": "Kitty", "age": 2 }, { "type": "dog", "name": "Yummy", "age": 3 } ] }
您可以透過將 JSON 資料的鍵和值傳送到 Builder 來重建或格式化 JSON 資料。請參閱範例/03.reformat。
{ // ?Example: minify json b := iterjson.NewBuilder("", "") for item, err := range iterjson.Parse(data) { errorz.MustZ(err) b.AddRaw(item.Key, item.Token) } out := errorz.Must(b.Bytes()) fmt.Printf("\n--- minify ---\n%s\n----------\n", out) } { // ?Example: format json b := iterjson.NewBuilder("? ", "\t") for item, err := range iterjson.Parse(data) { errorz.MustZ(err) b.AddRaw(item.Key, item.Token) } out := errorz.Must(b.Bytes()) fmt.Printf("\n--- reformat ---\n%s\n----------\n", out) }
第一個範例縮小了 JSON,而第二個範例則使用前綴「?」對其進行格式化。每行。
--- minify --- {"name":"Alice","age":24,"scores":[9,10,8],"address":{"city":"The Sun","zip":10101}} ---------- --- reformat --- ? { ? "name": "Alice", ? "age": 24, ? "scores": [ ? 9, ? 10, ? 8 ? ], ? "address": { ? "city": "The Sun", ? "zip": 10101 ? } ? } ----------
在此範例中,我們透過在 fmt.Fprintf() 呼叫之前新增 b.WriteNewline() 來將行號新增至 JSON 輸出。請參閱範例/04.line_number。
// ?Example: print with line number i := 0 b := iterjson.NewBuilder("", " ") for item, err := range iterjson.Parse(data) { i++ errorz.MustZ(err) b.WriteNewline(item.Token.Type()) // ? add line number fmt.Fprintf(b, "%3d ", i) b.Add(item.Key, item.Token) } out := errorz.Must(b.Bytes()) fmt.Printf("\n--- line number ---\n%s\n----------\n", out)
這將輸出:
1 { 2 "name": "Alice", 3 "age": 24, 4 "scores": [ 5 9, 6 10, 7 8 8 ], 9 "address": { 10 "city": "The Sun", 11 "zip": 10101 12 } 13 }
透過在 b.WriteComma() 和 b.WriteNewline() 之間放置 fmt.Fprintf(comment),您可以在每行末尾新增註解。請參閱範例/05.comment。
i, newlineIdx, maxIdx := 0, 0, 30 b := iterjson.NewBuilder("", " ") for item, err := range iterjson.Parse(data) { errorz.MustZ(err) b.WriteComma(item.Token.Type()) // ? add comment if i > 0 { length := b.Len() - newlineIdx fmt.Fprint(b, strings.Repeat(" ", maxIdx-length)) fmt.Fprintf(b, "// %2d", i) } i++ b.WriteNewline(item.Token.Type()) newlineIdx = b.Len() // save the newline index b.Add(item.Key, item.Token) } length := b.Len() - newlineIdx fmt.Fprint(b, strings.Repeat(" ", maxIdx-length)) fmt.Fprintf(b, "// %2d", i) out := errorz.Must(b.Bytes()) fmt.Printf("\n--- comment ---\n%s\n----------\n", out)
這將輸出:
{ // 1 "name": "Alice", // 2 "age": 24, // 3 "scores": [ // 4 9, // 5 10, // 6 8 // 7 ], // 8 "address": { // 9 "city": "The Sun", // 10 "zip": 10101 // 11 } // 12 } // 13
有item.GetPathString()和item.GetRawPath()來取得目前item的路徑。您可以使用它們來過濾 JSON 資料。請參閱範例/06.filter_print。
使用 item.GetPathString() 和正規表示式的範例:
fmt.Printf("\n--- filter: GetPathString() ---\n") i := 0 for item, err := range iterjson.Parse(data) { i++ errorz.MustZ(err) path := item.GetPathString() switch { case path == "name", strings.Contains(path, "address"): // continue default: continue } // ? print with line number fmt.Printf("%2d %20s . %s\n", i, item.Token, item.GetPath()) }
item.GetRawPath() 和 path.Match() 範例:
fmt.Printf("\n--- filter: GetRawPath() ---\n") i := 0 for item, err := range iterjson.Parse(data) { i++ errorz.MustZ(err) path := item.GetRawPath() switch { case path.Match("name"), path.Contains("address"): // continue default: continue } // ? print with line number fmt.Printf("%2d %20s . %s\n", i, item.Token, item.GetPath()) }
兩個範例都會輸出:
{ "name": "Alice", "age": 24, "scores": [9, 10, 8], "address": { "city": "The Sun", "zip": 10101 } }
透過將 Builder 與選項 SetSkipEmptyStructures(false) 和過濾邏輯結合,您可以過濾 JSON 資料並傳回新的 JSON。請參閱範例/07.filter_json
package main import ( "fmt" "ezpkg.io/errorz" iterjson "ezpkg.io/iter.json" ) func main() { data := `{"name": "Alice", "age": 24, "scores": [9, 10, 8], "address": {"city": "The Sun", "zip": 10101}}` // ?Example: iterate over json fmt.Printf("| %12v | %10v | %10v |%v|\n", "PATH", "KEY", "TOKEN", "LVL") fmt.Println("| ------------ | ---------- | ---------- | - |") for item, err := range iterjson.Parse([]byte(data)) { errorz.MustZ(err) fmt.Printf("| %12v | %10v | %10v | %v |\n", item.GetPathString(), item.Key, item.Token, item.Level) } }
此範例將傳回一個僅包含過濾欄位的新 JSON:
| PATH | KEY | TOKEN |LVL| | ------------ | ---------- | ---------- | - | | | | { | 0 | | name | "name" | "Alice" | 1 | | age | "age" | 24 | 1 | | scores | "scores" | [ | 1 | | scores.0 | | 9 | 2 | | scores.1 | | 10 | 2 | | scores.2 | | 8 | 2 | | scores | | ] | 1 | | address | "address" | { | 1 | | address.city | "city" | "The Sun" | 2 | | address.zip | "zip" | 10101 | 2 | | address | | } | 1 | | | | } | 0 |
這是編輯 JSON 資料中的值的範例。假設我們的 API 使用數字 ID。 id 太大,JavaScript 無法處理它們。我們需要將它們轉換為字串。請參閱範例/08.number_id 和 order.json。
迭代 JSON 數據,找到所有 _id 欄位並將數字 id 轉換為字串:
b := iterjson.NewBuilder("", " ") // open an object b.Add("", iterjson.TokenObjectOpen) // add a few fields b.Add("name", "Alice") b.Add("age", 22) b.Add("email", "alice@example.com") b.Add("phone", "(+84) 123-456-789") // open an array b.Add("languages", iterjson.TokenArrayOpen) b.Add("", "English") b.Add("", "Vietnamese") b.Add("", iterjson.TokenArrayClose) // close the array // accept any type that can marshal to json b.Add("address", Address{ HouseNumber: 42, Street: "Ly Thuong Kiet", City: "Ha Noi", Country: "Vietnam", }) // accept []byte as raw json b.Add("pets", []byte(`[{"type":"cat","name":"Kitty","age":2},{"type":"dog","name":"Yummy","age":3}]`)) // close the object b.Add("", iterjson.TokenObjectClose) out := errorz.Must(b.Bytes()) fmt.Printf("\n--- build json ---\n%s\n", out)
這將為數字 id 加上引號:
{ "name": "Alice", "age": 22, "email": "alice@example.com", "phone": "(+84) 123-456-789", "languages": [ "English", "Vietnamese" ], "address": {"house_number":42,"street":"Ly Thuong Kiet","city":"Ha Noi","country":"Vietnam"}, "pets": [ { "type": "cat", "name": "Kitty", "age": 2 }, { "type": "dog", "name": "Yummy", "age": 3 } ] }
ezpkg.io/iter.json 套件使 Go 開發人員能夠精確且有效率地處理 JSON 資料。無論您需要迭代複雜的 JSON 結構、動態建立新的 JSON 物件、格式化或縮小資料、過濾特定字段,甚至轉換值,iter.json 都提供了靈活而強大的解決方案。
我很高興與社群分享這個套件作為有效 JSON 操作的工具,而無需完全解析資料。雖然它仍處於早期開發階段,並且還有更多功能的空間,但它已經適用於許多常見用例。
如果您有具體的要求或改進想法,請隨時與我們聯繫 - 我很樂意聽到您的反饋並幫助支持您的用例! ?
我是奧利佛‧阮 。 一名使用 Go 和 JS 的軟體工程師。我喜歡每天學習並看到更好的自己。偶爾會衍生出新的開源專案。在我的旅程中分享知識和想法。
該貼文也發佈在 olivernguyen.io。
以上是iter.json:在 Go 中迭代和操作 JSON 的強大而有效的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!