paint-brush
Нова функція в Go спрощує ітерацію та маніпулювання даними JSONза@olvrng
Нова історія

Нова функція в Go спрощує ітерацію та маніпулювання даними JSON

за Oliver Nguyen13m2024/12/17
Read on Terminal Reader

Надто довго; Читати

Що, якби ви могли прокрутити свої дані JSON, захопити шлях до кожного елемента та вирішити, що саме з ним робити на льоту?
featured image - Нова функція в Go спрощує ітерацію та маніпулювання даними JSON
Oliver Nguyen HackerNoon profile picture

Вам коли-небудь доводилося змінювати неструктуровані дані JSON у Go? Можливо, вам доводилося видалити пароль і всі поля з чорного списку, перейменувати ключі з camelCase на snake_case або перетворити всі ідентифікатори номерів на рядки, оскільки JavaScript не любить int64 ? Якщо ваше рішення полягало в тому, щоб демаршалювати все в map[string]any за допомогою encoding/json , а потім маршалювати назад... ну, погодьтеся, це далеко не ефективно!


Що, якби ви могли прокрутити дані JSON, захопити шлях до кожного елемента та вирішити, що саме з ним робити на льоту?


так! У мене хороша новина! Завдяки новій функції ітератора в Go 1.23 є кращий спосіб ітерації та обробки JSON. Зустрічайте ezpkg.io/iter.json — ваш потужний і ефективний компаньйон для роботи з JSON у Go.


1. Ітерація JSON

Враховуючи, що у нас є файл alice.json :

 { "name": "Alice", "age": 24, "scores": [9, 10, 8], "address": { "city": "The Sun", "zip": 10101 } }


Спочатку скористаємося for range Parse() , щоб переглянути файл JSON, а потім надрукуємо шлях, ключ, маркер і рівень кожного елемента. Див. examples/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 |

2. Створення JSON

Використовуйте Builder для створення даних JSON. Він приймає додаткові аргументи для відступу. Див. examples/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 } ] }

3. Форматування JSON

Ви можете реконструювати або форматувати дані JSON, надіславши їх ключ і значення до Builder . Див. examples/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 👉 } 👉 } ----------

4. Додавання номерів рядків

У цьому прикладі ми додаємо номери рядків до виводу 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 }

5. Додавання коментарів

Розмістивши 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

6. Фільтрування JSON і вилучення значень

Існують 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

7. Фільтрування JSON і повернення нового JSON

Поєднавши Builder з опцією SetSkipEmptyStructures(false) і логікою фільтрації, ви можете фільтрувати дані JSON і повертати новий JSON. Див. examples/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 } }

8. Редагування значень

Це приклад редагування значень у даних 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 з точністю та ефективністю. Незалежно від того, чи потрібно вам переглядати складні структури JSON, динамічно створювати нові об’єкти JSON, форматувати чи зменшувати дані, фільтрувати певні поля чи навіть перетворювати значення, iter.json пропонує гнучке та потужне рішення.


Я радий поділитися цим пакетом зі спільнотою як інструментом для ефективного маніпулювання JSON без необхідності повного аналізу даних. Незважаючи на те, що він все ще знаходиться на ранній стадії розробки, і є місце для додаткових функцій, він уже добре працює в багатьох поширених випадках використання.


Якщо у вас є конкретні вимоги чи ідеї щодо покращення, не соромтеся зв’язатися — я буду радий почути ваш відгук і допомогти підтримати ваші варіанти використання! 🥳



автор

Я Олівер Нгуєн. Інженер-програміст, який працює з Go та JS. Мені подобається навчатися та бачити кращу версію себе щодня. Час від часу створюйте нові проекти з відкритим кодом. Поділіться знаннями та думками під час моєї подорожі.


Пост також опубліковано на olivernguyen.io .

L O A D I N G
. . . comments & more!

About Author

Oliver Nguyen HackerNoon profile picture
Oliver Nguyen@olvrng
I’m a software maker working mostly in Go and JavaScript. Share knowledge and thoughts during my journey.

ПОВІСИТИ БИРКИ

ЦЯ СТАТТЯ БУЛА ПРЕДСТАВЛЕНА В...