Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-json

golang语言解析json

在 Golang 中解析 JSON 时,如果不需要解析整个 JSON 串,可以使用 encoding/json 包提供的 json.Decoder 来进行流式解析。这样可以避免将整个 JSON 数据加载到内存中,从而提高解析速度和减少内存消耗。

以下是使用 json.Decoder 进行流式解析的示例,只提取需要的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
)

func main() {
jsonStr := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockTime": 1619462861,
"transactions": [
{"transaction": "tx1"},
{"transaction": "tx2"}
]
}
}`

// 将 JSON 字符串转换为 io.Reader
reader := bytes.NewReader([]byte(jsonStr))
decoder := json.NewDecoder(reader)

var blockTime int64
var found bool

// 解析 JSON 流
for {
// 读取下一个 token
t, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Error decoding JSON: %v", err)
}

// 查找需要的字段
if t == "blockTime" {
// 解码 blockTime 字段的值
err := decoder.Decode(&blockTime)
if err != nil {
log.Fatalf("Error decoding blockTime: %v", err)
}
found = true
break
}
}

if found {
fmt.Printf("Block Time: %d\n", blockTime)
} else {
fmt.Println("Block Time not found")
}
}

解释:

  1. json.NewDecoder: 创建一个新的 json.Decoder 实例。
  2. decoder.Token: 逐个读取 JSON 流中的 token。
  3. decoder.Decode: 当找到需要的字段时,解析其值。

通过这种方式,你可以只解析需要的部分 JSON 数据,而不是解析整个 JSON 串,从而提高效率。

进一步优化

  • 只解析特定字段:通过 json.Decoder,你可以精确控制解析过程,避免不必要的解析。
  • 并行解析:如果有大量 JSON 数据需要解析,可以使用并行处理的方法进一步提高速度。

以下是一个并行解析的示例,假设你有多个 JSON 数据需要解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package main

import (
"bytes"
"encoding/json"
"fmt"
"log"
"sync"
)

func main() {
jsonStrings := []string{
`{"blockTime": 1619462861, "otherField": "value1"}`,
`{"blockTime": 1619462862, "otherField": "value2"}`,
// 添加更多 JSON 字符串
}

var wg sync.WaitGroup
results := make(chan int64, len(jsonStrings))

for _, jsonString := range jsonStrings {
wg.Add(1)
go func(jsonStr string) {
defer wg.Done()

reader := bytes.NewReader([]byte(jsonStr))
decoder := json.NewDecoder(reader)

var blockTime int64
var found bool

for {
t, err := decoder.Token()
if err == io.EOF {
break
}
if err != nil {
log.Printf("Error decoding JSON: %v", err)
return
}

if t == "blockTime" {
err := decoder.Decode(&blockTime)
if err != nil {
log.Printf("Error decoding blockTime: %v", err)
return
}
found = true
break
}
}

if found {
results <- blockTime
}
}(jsonString)
}

wg.Wait()
close(results)

for result := range results {
fmt.Printf("Block Time: %d\n", result)
}
}

通过这种方式,可以显著提高解析大量 JSON 数据的效率。

如果你知道 JSON 的结构和你需要的字段路径,可以使用 encoding/json 包中的结构体映射来高效地解析特定的数据。通过定义结构体,你可以直接映射到你需要的数据字段,而无需解析整个 JSON 对象。

以下是一个示例,展示了如何根据已知的 JSON 结构来解析特定的字段:

示例 JSON

假设你的 JSON 数据如下:

1
2
3
4
5
6
7
8
9
10
11
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockTime": 1619462861,
"transactions": [
{"transaction": "tx1"},
{"transaction": "tx2"}
]
}
}

定义结构体

你可以定义结构体以匹配 JSON 数据的结构,并只解析你需要的字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"encoding/json"
"fmt"
"log"
)

type Transaction struct {
Transaction string `json:"transaction"`
}

type Result struct {
BlockTime int64 `json:"blockTime"`
Transactions []Transaction `json:"transactions"`
}

type Response struct {
Jsonrpc string `json:"jsonrpc"`
Id int `json:"id"`
Result Result `json:"result"`
}

func main() {
jsonStr := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockTime": 1619462861,
"transactions": [
{"transaction": "tx1"},
{"transaction": "tx2"}
]
}
}`

var response Response

// 解析 JSON 数据到结构体
err := json.Unmarshal([]byte(jsonStr), &response)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}

// 访问特定的数据字段
fmt.Printf("Block Time: %d\n", response.Result.BlockTime)
for _, tx := range response.Result.Transactions {
fmt.Printf("Transaction: %s\n", tx.Transaction)
}
}

说明

  1. 定义结构体: 根据 JSON 数据的结构定义结构体 Response, Result, 和 Transaction
  2. 解析 JSON: 使用 json.Unmarshal 将 JSON 字符串解析到结构体中。
  3. 访问字段: 直接通过结构体字段访问特定的数据。

通过这种方法,你可以高效地解析特定字段,而不需要解析整个 JSON 对象。结构体映射不仅使代码更加清晰,也有助于提高解析速度。

是的,定义结构体并只解析你需要的字段是高效解析 JSON 的一种方式,但如果你完全不定义字段,而是使用 json.RawMessagemap[string]interface{},这也可以提高解析速度,特别是当你只关心 JSON 中的某些特定部分时。

使用 json.RawMessage

json.RawMessage 是一种可以延迟解码的方式,它允许你在初步解析时直接获取原始 JSON 数据,以后再进行详细解析。这样可以避免不必要的解码过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"encoding/json"
"fmt"
"log"
)

func main() {
jsonStr := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockTime": 1619462861,
"transactions": [
{"transaction": "tx1"},
{"transaction": "tx2"}
]
}
}`

type Response struct {
Result json.RawMessage `json:"result"`
}

var response Response

// 解析 JSON 数据到结构体
err := json.Unmarshal([]byte(jsonStr), &response)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}

// 解析 `result` 字段
var result map[string]interface{}
err = json.Unmarshal(response.Result, &result)
if err != nil {
log.Fatalf("Error unmarshaling result: %v", err)
}

// 访问特定的数据字段
blockTime := result["blockTime"].(float64) // JSON 数字类型解析为 float64
fmt.Printf("Block Time: %f\n", blockTime)

transactions := result["transactions"].([]interface{})
for _, tx := range transactions {
txMap := tx.(map[string]interface{})
fmt.Printf("Transaction: %s\n", txMap["transaction"].(string))
}
}

使用 map[string]interface{}

如果你知道 JSON 的结构较简单,并且只需要获取特定的字段,可以使用 map[string]interface{} 来解析 JSON,这种方式可以避免定义详细的结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"encoding/json"
"fmt"
"log"
)

func main() {
jsonStr := `{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockTime": 1619462861,
"transactions": [
{"transaction": "tx1"},
{"transaction": "tx2"}
]
}
}`

var data map[string]interface{}

// 解析 JSON 数据到 map
err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}

// 访问特定的数据字段
result := data["result"].(map[string]interface{})
blockTime := result["blockTime"].(float64)
fmt.Printf("Block Time: %f\n", blockTime)

transactions := result["transactions"].([]interface{})
for _, tx := range transactions {
txMap := tx.(map[string]interface{})
fmt.Printf("Transaction: %s\n", txMap["transaction"].(string))
}
}

总结

  • 使用结构体: 如果你知道 JSON 的结构并且字段较为固定,定义结构体可以使代码更清晰,且只解析需要的字段。
  • 使用 json.RawMessagemap[string]interface{}: 当你只关心 JSON 的一部分,或者 JSON 结构不固定时,这些方法可以提高解析效率,因为你可以先快速读取原始数据或仅处理部分数据。

选择合适的方法取决于你的具体需求和 JSON 数据的复杂性。