首页 > 后端开发 > Golang > iter.json:在 Go 中迭代和操作 JSON 的强大而有效的方法

iter.json:在 Go 中迭代和操作 JSON 的强大而有效的方法

Barbara Streisand
发布: 2024-12-27 15:25:10
原创
796 人浏览过

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

您是否曾经需要在 Go 中修改非结构化 JSON 数据?也许您必须删除所有列入黑名单的字段,将键从camelCase重命名为snake_case,或者将所有数字ID转换为字符串,因为JavaScript不喜欢int64? 如果您的解决方案是使用编码/json 将所有内容解组到 map[string]any 中,然后将其编组回去...好吧,让我们面对现实吧,这远非高效!

如果您可以循环遍历 JSON 数据,获取每个项目的路径,并即时决定如何处理它,会怎么样?

是的!我有一个好消息!借助 Go 1.23 中的新迭代器功能,有更好的方法来迭代和操作 JSON。来认识一下 ezpkg.io/iter.json — 在 Go 中使用 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

使用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
        }
    ]
}
登录后复制
登录后复制

3.格式化JSON

您可以通过将 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
?       }
?   }
----------
登录后复制

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.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()来获取当前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
  }
}
登录后复制
登录后复制

7. 过滤 JSON 并返回新的 JSON

通过将 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 |
登录后复制
登录后复制

8. 编辑值

这是编辑 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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板