golang高性能JSON灵活解组插件库marshmallow的使用

golang高性能JSON灵活解组插件库marshmallow的使用

Marshmallow是一个提供简单API来执行灵活且高性能JSON解组的Go语言包。它特别擅长处理非结构化数据 - 当某些字段已知而某些字段未知时,无需额外编码且零性能开销。

Marshmallow Campfire

安装

go get -u github.com/perimeterx/marshmallow

使用示例

package main

import (
	"fmt"
	"github.com/perimeterx/marshmallow"
)

func main() {
	v := struct {
		Foo string `json:"foo"`
		Boo []int  `json:"boo"`
	}{}
	result, err := marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3],"goo":12.6}`), &v)
	fmt.Printf("v=%+v, result=%+v, err=%v", v, result, err)
	// 输出: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar goo:12.6], err=<nil>
}

性能基准

Marshmallow在处理混合数据(部分字段已知,部分未知)时表现最佳。以下是性能对比:

Benchmark 迭代次数 每次迭代时间 分配字节数 分配次数
unmarshall twice 228693 5164 ns/op 1640 B/op 51 allocs/op
raw map 232236 5116 ns/op 2296 B/op 53 allocs/op
go codec 388442 3077 ns/op 2512 B/op 37 allocs/op
marshmallow 626168 1853 ns/op 608 B/op 18 allocs/op
marshmallow without populating struct 678616 1751 ns/op 608 B/op 18 allocs/op

marshmallow性能比较

何时使用Marshmallow

Marshmallow最适合您对输入数据感兴趣但仅预先知道其中一部分信息的用例。例如,如果您计划引用数据中的两个特定字段,然后遍历所有数据并应用一些通用逻辑。

使用原生库的示例:

func isAllowedToDrive(data []byte) (bool, error) {
	result := make(map[string]interface{})
	err := json.Unmarshal(data, &result)
	if err != nil {
		return false, err
	}

	age, ok := result["age"]
	if !ok {
		return false, nil
	}
	a, ok := age.(float64)
	if !ok {
		return false, nil
	}
	if a < 17 {
		return false, nil
	}

	hasDriversLicense, ok := result["has_drivers_license"]
	if !ok {
		return false, nil
	}
	h, ok := hasDriversLicense.(bool)
	if !ok {
		return false, nil
	}
	if !h {
		return false, nil
	}

	for key := range result {
		if strings.Contains(key, "prior_conviction") {
			return false, nil
		}
	}

	return true, nil
}

使用marshmallow的示例:

func isAllowedToDrive(data []byte) (bool, error) {
	v := struct {
		Age               int  `json:"age"`
		HasDriversLicense bool `json:"has_drivers_license"`
	}{}
	result, err := marshmallow.Unmarshal(data, &v)
	if err != nil {
		return false, err
	}

	if v.Age < 17 || !v.HasDriversLicense {
		return false, nil
	}

	for key := range result {
		if strings.Contains(key, "prior_conviction") {
			return false, nil
		}
	}

	return true, nil
}

API

Marshmallow公开了两个主要API函数 - UnmarshalUnmarshalFromJSONMap。在解组时,marshmallow支持以下可选选项:

  • 使用WithMode函数设置处理无效数据的模式
  • 使用WithExcludeKnownFieldsFromMap函数从结果映射中排除已知字段
  • 使用WithSkipPopulateStruct函数跳过结构体填充以提高性能

为了捕获未知的嵌套字段,结构体必须实现JSONDataErrorHandler

Marshmallow还支持使用EnableCacheEnableCustomCache缓存反射信息。

Marshmallow Logo

Marshmallow Logo


更多关于golang高性能JSON灵活解组插件库marshmallow的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能JSON灵活解组插件库marshmallow的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang高性能JSON灵活解组插件库marshmallow使用指南

marshmallow是一个高性能的Go语言JSON解组库,提供了比标准库encoding/json更灵活和高效的处理方式。下面我将详细介绍其特性和使用方法。

主要特性

  1. 高性能:比标准库更快
  2. 灵活解组:支持部分解组和未知字段处理
  3. 严格模式:可配置是否允许未知字段
  4. 轻量级:无外部依赖

安装

go get github.com/PerimeterX/marshmallow

基本使用

1. 简单解组

package main

import (
	"fmt"
	"github.com/PerimeterX/marshmallow"
)

type User struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

func main() {
	data := []byte(`{"name":"Alice","age":25,"extra":"field"}`)
	
	// 解组到结构体
	var user User
	result, err := marshmallow.Unmarshal(data, &user)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("User: %+v\n", user)         // User: {Name:Alice Age:25}
	fmt.Printf("All data: %+v\n", result)   // map[name:Alice age:25 extra:field]
}

2. 灵活解组模式

func flexibleUnmarshal() {
	data := []byte(`{"name":"Bob","age":30,"city":"New York"}`)
	
	// 使用灵活模式解组
	result, err := marshmallow.Unmarshal(data, nil)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("Result: %+v\n", result)  // map[name:Bob age:30 city:New York]
	
	// 获取特定字段
	if name, ok := result["name"].(string); ok {
		fmt.Println("Name:", name)  // Name: Bob
	}
}

3. 严格模式

func strictUnmarshal() {
	data := []byte(`{"name":"Charlie","age":35,"unknown":true}`)
	
	type Person struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}
	
	var person Person
	_, err := marshmallow.Unmarshal(
		data, 
		&person,
		marshmallow.WithStrictMode(true), // 启用严格模式
	)
	
	// 会报错,因为包含未知字段"unknown"
	fmt.Println("Error:", err)  // Error: found unknown field: unknown
}

高级用法

1. 部分解组

func partialUnmarshal() {
	data := []byte(`{
		"user": {"name":"David","age":40},
		"metadata": {"created":"2023-01-01","modified":"2023-02-01"}
	}`)
	
	// 只解组user部分
	var user struct {
		Name string `json:"name"`
		Age  int    `json:"age"`
	}
	
	result, err := marshmallow.Unmarshal(data, &user, marshmallow.WithExcludeKnownFieldsFromMap(true))
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("User: %+v\n", user)  // User: {Name:David Age:40}
	fmt.Printf("Metadata: %+v\n", result["metadata"])  // map[created:2023-01-01 modified:2023-02-01]
}

2. 性能优化模式

func performanceOptimizedUnmarshal() {
	data := []byte(`{"id":123,"name":"Eve","active":true}`)
	
	type Account struct {
		ID     int    `json:"id"`
		Name   string `json:"name"`
		Active bool   `json:"active"`
	}
	
	var account Account
	_, err := marshmallow.Unmarshal(
		data,
		&account,
		marshmallow.WithMode(marshmallow.ModeFailOnFirstError), // 性能优化模式
	)
	
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("Account: %+v\n", account)  // Account: {ID:123 Name:Eve Active:true}
}

性能对比

marshmallow在大多数场景下比标准库encoding/json有更好的性能,特别是在以下情况:

  1. 处理大型JSON文档时
  2. 需要部分解组时
  3. 需要保留未知字段时

最佳实践

  1. 已知结构优先:对于已知结构的JSON,使用结构体解组
  2. 灵活处理未知字段:使用nil作为目标接收灵活结果
  3. 生产环境使用严格模式:避免意外字段导致的潜在问题
  4. 性能敏感场景使用优化模式ModeFailOnFirstError可提升性能

marshmallow为Go开发者提供了更灵活高效的JSON处理方案,特别适合需要高性能和灵活性的场景。

回到顶部