golang动态API客户端可定制JSON格式插件库dynjson的使用

golang动态API客户端可定制JSON格式插件库dynjson的使用

dynjson是一个允许API根据客户端选择返回特定字段的Go库。

介绍

dynjson允许API只返回客户端选择的字段:

GET https://api.example.com/v1/foos
[{"id":1,foo":1,"bar":2,"baz":3}]

GET https://api.example.com/v1/foos?select=foo
[{"foo":1}]

GET https://api.example.com/v1/foos/1?select=foo
{"foo":1}

dynjson会模仿原始结构体,使用原始类型和json标签。字段顺序与select参数相同。

安装

go get github.com/cocoonspaces/dynjson

示例

基本使用

type APIResult struct {
    Foo int     `json:"foo"`
    Bar string  `json:"bar"`
}

f := dynjson.NewFormatter()

res := &APIResult{Foo:1, Bar:"bar"}
o, err := f.Format(res, dynjson.FieldsFromRequest(r))
if err != nil {
    // 处理错误
}
err = json.NewEncoder(w).Encode(o) // 输出: {"foo": 1}

包含结构体字段

type APIResult struct {
    Foo int          `json:"foo"`
    Bar APIIncluded  `json:"bar"`
}

type APIIncluded struct {
    BarFoo int    `json:"barfoo"`
    BarBar string `json:"barbar"`
}

f := dynjson.NewFormatter()

res := &APIResult{Foo: 1, Bar: APIIncluded{BarFoo:1, BarBar: "bar"}}
o, err := f.Format(res, []string{"foo", "bar.barfoo"})
if err != nil {
    // 处理错误
}
err = json.NewEncoder(w).Encode(o) // 输出: {"foo": 1, "bar":{"barfoo": 1}}

使用切片

type APIResult struct {
    Foo int     `json:"foo"`
    Bar string  `json:"bar"`
}

f := dynjson.NewFormatter()

res := []APIResult{{Foo: 1, Bar: "bar"}}
o, err := f.Format(res, []string{"foo"})
if err != nil {
    // 处理错误
}
err = json.NewEncoder(w).Encode(o) // 输出: [{"foo": 1}]

包含切片字段

type APIResult struct {
    Foo int        `json:"foo"`
    Bar []APIItem  `json:"bar"`
}

type APIItem struct {
    BarFoo int    `json:"barfoo"`
    BarBar string `json:"barbar"`
}

f := dynjson.NewFormatter()

res := &APIResult{Foo: 1, Bar: []APIItem{{BarFoo: 1, BarBar: "bar"}}}
o, err := f.Format(res, []string{"foo", "bar.barfoo"})
if err != nil {
    // 处理错误
}
err = json.NewEncoder(w).Encode(o) // 输出: {"foo": 1, "bar":[{"barfoo": 1}]}

限制

  • 不支持没有json标签的匿名字段(由Go JSON编码器嵌入到封闭结构中的字段)
  • 不支持使用map_field_name.map_key过滤映射内容

性能影响

BenchmarkFormat_Fields
BenchmarkFormat_Fields-8     	 2466639	       480 ns/op	     184 B/op	       7 allocs/op
BenchmarkFormat_NoFields
BenchmarkFormat_NoFields-8   	 5255031	       232 ns/op	      32 B/op	       1 allocs/op
BenchmarkRawJSON
BenchmarkRawJSON-8           	 5351313	       223 ns/op	      32 B/op	       1 allocs/op

贡献指南

欢迎贡献,只要:

  • 包含单元测试和注释
  • 不使用外部包

许可证

MIT - 参见LICENSE


更多关于golang动态API客户端可定制JSON格式插件库dynjson的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang动态API客户端可定制JSON格式插件库dynjson的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用dynjson构建动态API客户端

dynjson是一个Go语言库,用于创建可动态处理不同JSON格式的API客户端。它特别适合需要与多个API交互或处理不断变化的API响应的场景。

基本安装

go get github.com/tidwall/dynjson

核心功能

dynjson提供动态解析JSON的能力,无需预先定义结构体,可以直接访问JSON数据。

基本用法示例

package main

import (
	"fmt"
	"github.com/tidwall/dynjson"
)

func main() {
	// 示例JSON数据
	jsonData := `{
		"name": "John Doe",
		"age": 30,
		"address": {
			"street": "123 Main St",
			"city": "Anytown"
		},
		"hobbies": ["reading", "hiking"]
	}`

	// 解析JSON
	data, err := dynjson.Parse([]byte(jsonData))
	if err != nil {
		panic(err)
	}

	// 访问数据
	name := data.Get("name").String()
	age := data.Get("age").Int()
	city := data.Get("address.city").String()
	hobby1 := data.Get("hobbies.0").String()

	fmt.Printf("Name: %s\nAge: %d\nCity: %s\nFirst hobby: %s\n", 
		name, age, city, hobby1)
}

高级特性

1. 动态路径访问

// 使用路径表达式访问嵌套字段
email := data.Get("contact.email.work").String()

2. 类型安全访问

// 安全获取值,带错误检查
age, err := data.Get("age").Int()
if err != nil {
	fmt.Println("Invalid age:", err)
}

3. 默认值处理

// 如果字段不存在,返回默认值
phone := data.Get("contact.phone").StringOr("N/A")

4. 动态修改JSON

// 修改JSON数据
data.Set("name", "Jane Doe")
data.Set("age", 31)
data.Set("address.city", "Newtown")

// 添加新字段
data.Set("contact.email", "jane@example.com")

// 删除字段
data.Del("hobbies")

// 获取修改后的JSON
modifiedJSON, _ := data.MarshalJSON()
fmt.Println(string(modifiedJSON))

构建动态API客户端

下面是一个完整的动态API客户端示例:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"github.com/tidwall/dynjson"
)

type DynamicAPIClient struct {
	BaseURL    string
	HTTPClient *http.Client
}

func NewDynamicAPIClient(baseURL string) *DynamicAPIClient {
	return &DynamicAPIClient{
		BaseURL:    baseURL,
		HTTPClient: &http.Client{},
	}
}

func (c *DynamicAPIClient) Get(path string) (dynjson.Data, error) {
	resp, err := c.HTTPClient.Get(c.BaseURL + path)
	if err != nil {
		return nil, fmt.Errorf("request failed: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response failed: %v", err)
	}

	return dynjson.Parse(body)
}

func (c *DynamicAPIClient) Post(path string, payload interface{}) (dynjson.Data, error) {
	jsonData, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("marshaling payload failed: %v", err)
	}

	resp, err := c.HTTPClient.Post(
		c.BaseURL+path,
		"application/json",
		bytes.NewBuffer(jsonData),
	)
	if err != nil {
		return nil, fmt.Errorf("request failed: %v", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response failed: %v", err)
	}

	return dynjson.Parse(body)
}

func main() {
	client := NewDynamicAPIClient("https://api.example.com")

	// 获取用户数据
	userData, err := client.Get("/users/123")
	if err != nil {
		panic(err)
	}

	fmt.Println("User email:", userData.Get("email").String())

	// 创建新订单
	orderResp, err := client.Post("/orders", map[string]interface{}{
		"product_id": 456,
		"quantity":   2,
	})
	if err != nil {
		panic(err)
	}

	fmt.Println("Order ID:", orderResp.Get("id").String())
}

自定义JSON处理

dynjson允许注册自定义类型处理器:

type CustomType struct {
	Field1 string
	Field2 int
}

// 注册自定义类型处理器
dynjson.RegisterType(func(v interface{}) (interface{}, bool) {
	if m, ok := v.(map[string]interface{}); ok {
		return &CustomType{
			Field1: m["field1"].(string),
			Field2: int(m["field2"].(float64)),
		}, true
	}
	return nil, false
})

// 使用自定义类型
data, _ := dynjson.Parse([]byte(`{"custom": {"field1": "value", "field2": 42}}`))
custom := data.Get("custom").Value().(*CustomType)
fmt.Printf("Custom: %+v\n", custom)

性能考虑

  1. 对于高性能场景,考虑缓存解析结果
  2. 批量处理数据时,使用流式处理
  3. 对于稳定的API,仍然推荐使用静态结构体

dynjson提供了灵活处理动态JSON数据的能力,特别适合构建需要与多种API交互的客户端应用。

回到顶部