ホームページ >バックエンド開発 >Golang >iter.json: Go で JSON を反復および操作するための強力かつ効率的な方法

iter.json: Go で JSON を反復および操作するための強力かつ効率的な方法

Barbara Streisand
Barbara Streisandオリジナル
2024-12-27 15:25:10799ブラウズ

iter.json: A Powerful and Efficient Way to Iterate and Manipulate JSON in Go

Go で非構造化 JSON データを変更する必要があったことがありますか? JavaScript が int64 を好まないため、ブラックリストに登録されたフィールドをすべて削除したり、キーの名前をキャメルケースからスネークケースに変更したり、すべての数値 ID を文字列に変換したりする必要があったのではないでしょうか? あなたの解決策が、encoding/json を使用してすべてを map[string]any にアンマーシャリングし、それを元にマーシャリングすることだった場合...まあ、正直に言って、それは効率的とは程遠いです!

JSON データをループして各項目のパスを取得し、それをどのように処理するかをその場で正確に決定できたらどうなるでしょうか?

はい!良いお知らせがあります! Go 1.23 の新しいイテレーター機能を使用すると、JSON を反復して操作するためのより良い方法が得られます。 Go で JSON を操作するための強力かつ効率的なコンパニオンである ezpkg.io/iter.json をご紹介します。


1. 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 |

2. JSONの構築

ビルダーを使用して 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
        }
    ]
}

3. JSON のフォーマット

JSON データのキーと値をビルダーに送信することで、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 を縮小し、2 番目の例ではプレフィックス「?」を付けてフォーマットします。各行に表示されます。

--- 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. 行番号の追加

この例では、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    }

5. コメントの追加

b.WriteComma()とb.WriteNewline()の間にfmt.Fprintf(comment)を入れると、各行の末尾にコメントを追加できます。例/05.コメントを参照してください。

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 データをフィルタリングできます。例/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
  }
}

7. JSON をフィルタリングして新しい JSON を返す

ビルダーとオプション 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 |

8. 値の編集

これは、JSON データの値を編集する例です。 API に数値 ID を使用していると仮定します。 ID が大きすぎるため、JavaScript では処理できません。それらを文字列に変換する必要があります。 example/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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。