shape README

The project's README from github.com/rhevorn/shape. As one markdown file: README.md.

shape

Type-safe validation, transformation, and JSON binding for Go.

shape lets you describe a struct once, then reuse that definition to:

The public API uses Go generics. Invalid field names and incompatible field types are rejected when a schema is created, while normal method and argument mistakes are caught by the Go compiler.

Install

go get github.com/rhevorn/shape

Requires Go 1.24 or newer. Runtime packages use only the standard library.

Quick start

This is a complete program:

package main

import (
	"fmt"

	"github.com/rhevorn/shape"
)

type User struct {
	Name string   `json:"name"`
	Age  int      `json:"age"`
	Tags []string `json:"tags"`
}

var userSchema = shape.New[User](
	shape.String("Name").Trim().NotEmpty().MaxLength(50),
	shape.Int("Age").Min(18).Max(120),
	shape.Slice("Tags", shape.String().Trim().NotEmpty()).
		NotEmpty().
		Unique(),
)

func main() {
	user, err := userSchema.ParseJSON([]byte(`{
		"name": " Pong ",
		"age": 20,
		"tags": [" go ", "shape"]
	}`))
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", user)
	// main.User{Name:"Pong", Age:20, Tags:[]string{"go", "shape"}}
}

Transformation steps keep the order in which methods are called, and validation rules do the same. A schema JSON operation completes all transformation steps before it starts validation, so NotEmpty sees the trimmed string.

One schema, three operations

Use the same schema with typed Go values or JSON:

raw := User{Name: " Pong ", Age: 20, Tags: []string{" go "}}

user, err := userSchema.Transform(raw)
err = userSchema.Validate(user)

user, err = userSchema.ParseJSON([]byte(`{
	"name": " Pong ",
	"age": 20,
	"tags": [" go "]
}`))

The responsibilities are deliberately separate:

All operations also have context-aware forms.

Struct tags

Tags are a shorter alternative for straightforward request structs:

type CreateUserRequest struct {
	Name string `json:"name" shape:"trim,notempty,maxlength=50"`
	Age  int    `json:"age" shape:"min=18,max=120"`
}

func decodeRequest(data []byte) (CreateUserRequest, error) {
	var request CreateUserRequest
	err := shape.BindJSON(&request, data)
	return request, err
}

BindJSON changes the destination only after decoding, transformation, and validation all succeed. Use shape.Struct[CreateUserRequest]() when the tagged schema needs to be stored or reused.

Tagged schemas can add cross-field behavior with shape.Struct[T]().Apply(...).Refine(...). Use explicit shape.New[T](...) schemas when field behavior should be written in Go rather than tags.

Validation without a schema

The validate package works independently:

username := validate.String().
	NotEmpty().
	MinLength(3).
	Pattern(`^[a-z0-9_]+$`)

err := username.Validate("pong")

It provides validators for strings, numbers, booleans, times, durations, pointers, slices, maps, and custom values.

Transformation without a schema

The transform package also works independently:

canonicalName := transform.String().
	Trim().
	ToLower()

name, err := canonicalName.Transform(" Pong ")
// name is "pong"

Transformers never perform validation. Custom transformations use Apply or ApplyContext.

JSON options

JSON decoding can reject unknown object fields and limit input size:

options := shape.JSONOptions{
	DisallowUnknownFields: true,
	MaxBytes:              1 << 20,
}

user, err := userSchema.ParseJSON(data, options)

Reader, context, Parse, and Bind variants are listed in the API contract.

Errors and languages

Validation errors contain stable issue codes and paths:

var validationError *validate.Error
if errors.As(err, &validationError) {
	for _, issue := range validationError.Issues {
		fmt.Println(issue.Code, issue.Path.String(), issue.Message)
	}
}

Built-in messages support validate.English and validate.SimplifiedChinese. A locale can be attached to a context with validate.WithLocale.

Static checking

Go checks fluent method names, generic types, callback signatures, and argument types. The optional shapevet analyzer additionally checks statically visible struct tags and explicit schema field names:

go install github.com/rhevorn/shape/tools/shapevet@latest
go vet -vettool="$(which shapevet)" ./...

shapevet is optional. Schema construction still rejects invalid definitions at runtime when the analyzer is not installed.

Documentation

JSON Schema Draft 2020-12 and OpenAPI 3.1 adapters are available in the jsonschema and openapi packages. See the export example.

GitHub topics

go, golang, validation, validator, schema-validation, data-validation, struct-tags, json-validation, data-normalization, type-safe

License

MIT

shape (rhevorn/shape)

Searching Database…

180 ·GitHub
这是什么?Shape 是一个类型安全、零依赖的 Go schema 校验库,让你只需描述一次 struct,就能复用它来进行转换、校验,以及把 JSON 解析为 Go 值。
它是框架还是库?它是一个由可组合的包(validate、transform、jsonschema、openapi)构成的 Go 库,公共 API 基于泛型,不是框架也不是独立应用。
怎么把它加到项目里?把它作为 Go 模块安装(需要 Go 1.24+),然后用 shape.NewT 或 struct 标签定义 schema,并在其上调用 Transform、Validate、ValidateFirst 或 ParseJSON/BindJSON。
运行它需要任何外部依赖吗?不需要,运行时包只依赖 Go 标准库,无需安装任何第三方依赖。
安装或 schema 方面有什么坑吗?无效的字段名和不兼容的字段类型在创建 schema 时就会被捕获;如果想在编译期做额外的标签/schema 检查,需要可选的 shapevet 静态分析器;没有它的话,错误的定义只能在运行时被发现。
校验是在第一个错误时就停止,还是收集所有错误?可以自选:Validate 会收集发现的每个问题,而 ValidateFirst 在遇到第一个问题时立即停止。
能从 shape schema 生成 JSON Schema 或 OpenAPI 定义吗?可以,jsonschema 包能从同一套 schema 定义生成 JSON Schema Draft 2020-12 输出,openapi 包则生成 OpenAPI 3.1 适配器。
校验错误信息只有英文吗?不是,内置错误信息开箱即支持多种语言,包括英文和简体中文,你可以通过 context 附加区域设置。
shapevet 是什么?shapevet 是一个可选的静态分析器,在编译期检查链式方法名、struct 标签和显式指定的 schema 字段名;没有它,无效的 schema 定义仍会在运行时被捕获。
Creator
rhevorn
Size
0.8 MB
Stars
180
Updated
CommitSep 20
Age
1mo
Commits · 90 Days