Golang解析JSON数据并实现分组统计的方法

Golang解析JSON数据并实现分组统计的方法 我有以下JSON字符串,想要统计唯一指标的数量,有没有简单的方法可以实现? 例如,从下面的字符串中,我想统计唯一的负载生成器数量,如下所示,结果是3个。

load_generator_84, load_generator_86, load_generator_89

我希望用尽可能少的代码来完成这个任务。

我可以这样获取数组: records, ok := result[“logEvents”].([]interface{})

之后,我在考虑使用一些正则表达式来获取计数?

你们觉得怎么样? 我认为对所有值使用for循环可能效率不高。 有什么建议吗?

JSON字符串 =

{
	"logEvents": [{
		"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_84\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_2\",\"load_generator_84\":0}",
		"timestamp": 1596838449873
	}, {
		"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_84\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_3\",\"load_generator_84\":0}",
		"timestamp": 1596838449873
	}, {
		"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_86\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_4\",\"load_generator_86\":0}",
		"timestamp": 1596838449873
	}, {
		"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_89\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_5\",\"load_generator_89\":0}",
		"timestamp": 1596838449873
	}, {
		"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_89\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_6\",\"load_generator_89\":0}",
		"timestamp": 1596838449873
	}]
}

更多关于Golang解析JSON数据并实现分组统计的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

希望这些内容都讲清楚了!

由于 Go 语言的静态类型特性,在 Go 中解析 JSON 可能比其他语言稍微困难一些。

不过我发现,使用 JSON to Go 工具将 JSON 转换成 Go 结构体,会让事情变得容易得多!

也有一个类似的用于 XML 的工具。

更多关于Golang解析JSON数据并实现分组统计的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


{ “logEvents”: [{ “message”: “{“bufferLib”:“Undefined”,”_slack":{“lbMetrics”:[{“Namespace”:“default”,“Dimensions”:[[“item_index”,“batch_index”,“bufferLib”],[“bufferLib”],[“bufferLib”,“item_index”],[“bufferLib”,“batch_index”]],“Metrics”:[{“Name”:“load_generator_84”,“Unit”:""}]}],“Timestamp”:1596838449873},“batch_index”:“batch_14185”,“item_index”:“item_2”,“load_generator_84”:0}", “timestamp”: 1596838449873 }

非常感谢 @ncw,这个网站对我极其有用。

shreyas_darwhatkar:

我有以下JSON字符串

这看起来是JSON中嵌套了JSON!

为了使其有用,你还需要将消息也解码为JSON。

我继续将其解析为 map[string]interface{} 等,最终得到了这个(playground

func main() {
	var data map[string]interface{}
	err := json.Unmarshal([]byte(in), &data)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(data)
	var foundNames = map[string]struct{}{}
	events := data["logEvents"].([]interface{})
	for _, event := range events {
		e := event.(map[string]interface{})
		message := e["message"].(string)
		var m map[string]interface{}
		err := json.Unmarshal([]byte(message), &m)
		if err != nil {
			log.Fatal(err)
		}
		slack := m["_slack"].(map[string]interface{})
		lbMetrics := slack["lbMetrics"].([]interface{})
		for _, lbMetric := range lbMetrics {
			metrics := lbMetric.(map[string]interface{})["Metrics"].([]interface{})
			for _, metric := range metrics {
				m := metric.(map[string]interface{})
				name := m["Name"].(string)
				fmt.Println(name)
				foundNames[name] = struct{}{}
			}
		}

	}
	fmt.Printf("found %d names", len(foundNames))

这真的很丑陋!

如果我来做这件事,我会像这样正确地解析JSON

type Message struct {
	BufferLib string `json:"bufferLib"`
	Slack     struct {
		LbMetrics []struct {
			Namespace  string     `json:"Namespace"`
			Dimensions [][]string `json:"Dimensions"`
			Metrics    []struct {
				Name string `json:"Name"`
				Unit string `json:"Unit"`
			} `json:"Metrics"`
		} `json:"lbMetrics"`
		Timestamp int64 `json:"Timestamp"`
	} `json:"_slack"`
	BatchIndex      string `json:"batch_index"`
	ItemIndex       string `json:"item_index"`
	LoadGenerator84 int    `json:"load_generator_84"`
}

// 一个包装结构体,以便我们可以将UnmarshalJSON方法附加到它上面
type JSONMessage struct {
	Message
}

// 解组字符串中的JSON
func (m *JSONMessage) UnmarshalJSON(b []byte) error {
	var messageString string
	if err := json.Unmarshal(b, &messageString); err != nil {
		return err
	}
	if err := json.Unmarshal([]byte(messageString), &m.Message); err != nil {
		return err
	}
	return nil
}

type Events struct {
	LogEvents []struct {
		Message   JSONMessage `json:"message"`
		Timestamp int64       `json:"timestamp"`
	} `json:"logEvents"`
}

(注意这个双重结构体技巧,它在我们解组内部Message时隐藏了UnmarshalJSON方法)。

然后主循环就变成了(playground

func main() {
	var data Events
	err := json.Unmarshal([]byte(in), &data)
	if err != nil {
		log.Fatal(err)
	}
	var foundNames = map[string]struct{}{}
	for _, logEvent := range data.LogEvents {
		for _, lbMetric := range logEvent.Message.Slack.LbMetrics {
			for _, metric := range lbMetric.Metrics {
				fmt.Println(metric.Name)
				foundNames[metric.Name] = struct{}{}
			}
		}
	}
	fmt.Printf("found = %d\n", len(foundNames))
}

请注意,我使用了mholt出色的JSON to Go工具来生成结构体定义——你只需粘贴JSON,它就会为你创建对应的Go结构体。

package main

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

func main() {
	jsonStr := `{
		"logEvents": [{
			"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_84\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_2\",\"load_generator_84\":0}",
			"timestamp": 1596838449873
		}, {
			"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_84\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_3\",\"load_generator_84\":0}",
			"timestamp": 1596838449873
		}, {
			"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_86\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_4\",\"load_generator_86\":0}",
			"timestamp": 1596838449873
		}, {
			"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_89\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_5\",\"load_generator_89\":0}",
			"timestamp": 1596838449873
		}, {
			"message": "{\"bufferLib\":\"Undefined\",\"_slack\":{\"lbMetrics\":[{\"Namespace\":\"default\",\"Dimensions\":[[\"item_index\",\"batch_index\",\"bufferLib\"],[\"bufferLib\"],[\"bufferLib\",\"item_index\"],[\"bufferLib\",\"batch_index\"]],\"Metrics\":[{\"Name\":\"load_generator_89\",\"Unit\":\"\"}]}],\"Timestamp\":1596838449873},\"batch_index\":\"batch_14185\",\"item_index\":\"item_6\",\"load_generator_89\":0}",
			"timestamp": 1596838449873
		}]
	}`

	var data map[string]interface{}
	json.Unmarshal([]byte(jsonStr), &data)

	uniqueGenerators := make(map[string]bool)
	re := regexp.MustCompile(`"Name":"(load_generator_\d+)"`)

	logEvents := data["logEvents"].([]interface{})
	for _, event := range logEvents {
		eventMap := event.(map[string]interface{})
		message := eventMap["message"].(string)
		
		matches := re.FindStringSubmatch(message)
		if len(matches) > 1 {
			uniqueGenerators[matches[1]] = true
		}
	}

	fmt.Printf("Unique load generators: %d\n", len(uniqueGenerators))
	fmt.Print("Generators: ")
	first := true
	for gen := range uniqueGenerators {
		if !first {
			fmt.Print(", ")
		}
		fmt.Print(gen)
		first = false
	}
}

输出结果:

Unique load generators: 3
Generators: load_generator_84, load_generator_86, load_generator_89

如果需要更精确的解析,可以使用结构体定义:

type LogEvent struct {
	Message   string `json:"message"`
	Timestamp int64  `json:"timestamp"`
}

type MessageData struct {
	Slack struct {
		LbMetrics []struct {
			Metrics []struct {
				Name string `json:"Name"`
			} `json:"Metrics"`
		} `json:"lbMetrics"`
	} `json:"_slack"`
}

func main() {
	var data struct {
		LogEvents []LogEvent `json:"logEvents"`
	}
	json.Unmarshal([]byte(jsonStr), &data)

	uniqueGenerators := make(map[string]bool)
	
	for _, event := range data.LogEvents {
		var msgData MessageData
		json.Unmarshal([]byte(event.Message), &msgData)
		
		for _, metric := range msgData.Slack.LbMetrics {
			for _, m := range metric.Metrics {
				if strings.HasPrefix(m.Name, "load_generator_") {
					uniqueGenerators[m.Name] = true
				}
			}
		}
	}

	fmt.Printf("Unique load generators: %d\n", len(uniqueGenerators))
	for gen := range uniqueGenerators {
		fmt.Println(gen)
	}
}
回到顶部