Ці даводзілася вам калі-небудзь змяняць неструктураваныя даныя JSON у Go? Магчыма, вам прыйшлося выдаліць пароль і ўсе палі ў чорным спісе, перайменаваць ключы з camelCase
у snake_case
або пераўтварыць усе ідэнтыфікатары лікаў у радкі, таму што JavaScript не любіць int64
? Калі вашым рашэннем было дэмаршалізаваць усё ў map[string]any
з дапамогай encoding/json
, а затым маршалізаваць гэта назад... ну, пагадзіцеся праўдзе ў вочы, гэта далёка не эфектыўна!
Што, калі б вы маглі пракруціць даныя JSON, захапіць шлях кожнага элемента і вырашыць, што менавіта з ім рабіць на хаду?
Так! У мяне добрая навіна! З новай функцыяй ітэратара ў Go 1.23 ёсць лепшы спосаб ітэрацыі і маніпулявання JSON. Знаёмцеся: ezpkg.io/iter.json — ваш магутны і эфектыўны кампаньён для працы з JSON у Go.
Улічваючы, што ў нас ёсць файл 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", "[email protected]") 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": "[email protected]", "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
. Глядзіце прыклады/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 👉 } 👉 } ----------
У гэтым прыкладзе мы дадаем нумары радкоў у вывад JSON, дадаўшы b.WriteNewline()
перад выклікам fmt.Fprintf()
. Глядзіце examples/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 }
Паставіўшы fmt.Fprintf(comment)
паміж b.WriteComma()
і b.WriteNewline()
, вы можаце дадаць каментарый у канцы кожнага радка. Глядзіце examples/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()
каб атрымаць шлях да бягучага элемента. Вы можаце выкарыстоўваць іх для фільтрацыі даных JSON. Глядзіце examples/06.filter_print .
Прыклад з item.GetPathString()
і regexp
:
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()) }
Абодва прыклады выводзяць:
2 "Alice" . name 9 { . address 10 "The Sun" . address.city 11 10101 . address.zip 12 } . address
Аб'яднаўшы Builder
з опцыяй SetSkipEmptyStructures(false)
і логікай фільтрацыі, вы можаце адфільтраваць даныя JSON і вярнуць новы JSON. Глядзіце прыклады/07.filter_json
// 🦁Example: filter and output json b := iterjson.NewBuilder("", " ") b.SetSkipEmptyStructures(true) // 👉 skip empty [] or {} for item, err := range iterjson.Parse(data) { errorz.MustZ(err) if item.Token.IsOpen() || item.Token.IsClose() { b.Add(item.Key, item.Token) continue } path := item.GetPathString() switch { case path == "name", strings.Contains(path, "address"): // continue default: continue } b.Add(item.Key, item.Token) } out := errorz.Must(b.Bytes()) fmt.Printf("\n--- filter: output json ---\n%s\n----------\n", out)
Гэты прыклад верне новы JSON толькі з адфільтраванымі палямі:
{ "name": "Alice", "address": { "city": "The Sun", "zip": 10101 } }
Гэта прыклад рэдагавання значэнняў у даных JSON. Выкажам здагадку, што мы выкарыстоўваем ідэнтыфікатары нумароў для нашага API. Ідэнтыфікатары занадта вялікія, і JavaScript не можа іх апрацаваць. Нам трэба пераўтварыць іх у радкі. Глядзіце examples/08.number_id і order.json .
Перабярыце даныя JSON, знайдзіце ўсе палі _id
і пераўтварыце лікавыя ідэнтыфікатары ў радкі:
b := iterjson.NewBuilder("", " ") for item, err := range iterjson.Parse(data) { errorz.MustZ(err) key, _ := item.GetRawPath().Last().ObjectKey() if strings.HasSuffix(key, "_id") { id, err0 := item.Token.GetInt() if err0 == nil { b.Add(item.Key, strconv.Itoa(id)) continue } } b.Add(item.Key, item.Token) } out := errorz.Must(b.Bytes()) fmt.Printf("\n--- convert number id ---\n%s\n----------\n", out)
Гэта дадасць двукоссі да ідэнтыфікатараў нумароў:
{ "order_id": "12345678901234", "number": 12, "customer_id": "12345678905678", "items": [ { "item_id": "12345678901042", "quantity": 1, "price": 123.45 }, { "item_id": "12345678901098", "quantity": 2, "price": 234.56 } ] }
Пакет ezpkg.io/iter.json дазваляе распрацоўшчыкам Go апрацоўваць даныя JSON з дакладнасцю і эфектыўнасцю. Iter.json прапануе гнуткае і магутнае рашэнне, незалежна ад таго, ці трэба вам перабіраць складаныя структуры JSON, дынамічна ствараць новыя аб'екты JSON, фарматаваць або мінімізаваць даныя, фільтраваць пэўныя палі або нават трансфармаваць значэнні.
Я рады падзяліцца з супольнасцю гэтым пакетам як інструментам для эфектыўнай працы з JSON без неабходнасці поўнага аналізу даных. Хоць ён усё яшчэ знаходзіцца на ранняй стадыі распрацоўкі і ёсць месца для дадатковых функцый, ён ужо добра працуе ў многіх звычайных выпадках выкарыстання.
Калі ў вас ёсць канкрэтныя патрабаванні або ідэі для паляпшэння, не саромейцеся звяртацца да нас — я буду рады пачуць вашы водгукі і дапамагчы падтрымаць вашы варыянты выкарыстання! 🥳
Я Олівер Нгуен. Інжынер-праграміст, які працуе з Go і JS. Мне падабаецца вучыцца і кожны дзень бачыць лепшую версію сябе. Час ад часу стварайце новыя праекты з адкрытым зыходным кодам. Дзяліцеся ведамі і думкамі падчас майго падарожжа.
Паведамленне таксама апублікавана на