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)
性能考虑
- 对于高性能场景,考虑缓存解析结果
- 批量处理数据时,使用流式处理
- 对于稳定的API,仍然推荐使用静态结构体
dynjson提供了灵活处理动态JSON数据的能力,特别适合构建需要与多种API交互的客户端应用。