Go Tagexpr
An interesting go struct tag expression syntax for field validation, etc.
Install / Use
npx skills add bytedance/go-tagexprInstalls into whichever agent you are using.
README
go-tagexpr

An interesting go struct tag expression syntax for field validation, etc.
Usage
-
Validator: A powerful validator that supports struct tag expression
-
Binding: A powerful HTTP request parameters binder that supports struct tag expression
Feature
- Support for a variety of common operator
- Support for accessing arrays, slices, members of the dictionary
- Support access to any field in the current structure
- Support access to nested fields, non-exported fields, etc.
- Support variable
- Support registers function expression
- Built-in len, sprintf, regexp functions
- Support single mode and multiple mode to define expression
- Parameter check subpackage
- Use offset pointers to directly take values, better performance
- Required go version ≥1.9
Example
package tagexpr_test
import (
"fmt"
tagexpr "github.com/bytedance/go-tagexpr/v2"
)
func Example() {
type T struct {
A int `tagexpr:"$<0||$>=100"`
B string `tagexpr:"len($)>1 && regexp('^\\w*$')"`
C bool `tagexpr:"expr1:(f.g)$>0 && $; expr2:'C must be true when T.f.g>0'"`
d []string `tagexpr:"@:len($)>0 && $[0]=='D'; msg:sprintf('invalid d: %v',$)"`
e map[string]int `tagexpr:"len($)==$['len']"`
e2 map[string]*int `tagexpr:"len($)==$['len']"`
f struct {
g int `tagexpr:"$"`
}
h int `tagexpr:"$>minVal"`
}
vm := tagexpr.New("tagexpr")
t := &T{
A: 107,
B: "abc",
C: true,
d: []string{"x", "y"},
e: map[string]int{"len": 1},
e2: map[string]*int{"len": new(int)},
f: struct {
g int `tagexpr:"$"`
}{1},
h: 10,
}
tagExpr, err := vm.Run(t)
if err != nil {
panic(err)
}
fmt.Println(tagExpr.Eval("A"))
fmt.Println(tagExpr.Eval("B"))
fmt.Println(tagExpr.Eval("C@expr1"))
fmt.Println(tagExpr.Eval("C@expr2"))
if !tagExpr.Eval("d").(bool) {
fmt.Println(tagExpr.Eval("d@msg"))
}
fmt.Println(tagExpr.Eval("e"))
fmt.Println(tagExpr.Eval("e2"))
fmt.Println(tagExpr.Eval("f.g"))
fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 9}))
fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 11}))
// Output:
// true
// true
// true
// C must be true when T.f.g>0
// invalid d: [x y]
// true
// false
// 1
// true
// false
}
Syntax
Struct tag syntax spec:
type T struct {
// Single model
Field1 T1 `tagName:"expression"`
// Multiple model
Field2 T2 `tagName:"exprName:expression; [exprName2:expression2;]..."`
// Omit it
Field3 T3 `tagName:"-"`
// Omit it when it is nil
Field4 T4 `tagName:"?"`
...
}
NOTE: The exprName under the same struct field cannot be the same!
|Operator or Operand|Explain|
|-----|---------|
|true false|boolean|
|0 0.0|float64 "0"|
|''|String|
|\\'| Escape ' delims in string|
|\"| Escape " delims in string|
|nil|nil, undefined|
|!|not|
|+|Digital addition or string splicing|
|-|Digital subtraction or negative|
|*|Digital multiplication|
|/|Digital division|
|%|division remainder, as: float64(int64(a)%int64(b))|
|==|eq|
|!=|ne|
|>|gt|
|>=|ge|
|<|lt|
|<=|le|
|&&|Logic and|
|\|\||Logic or|
|()|Expression group|
|(X)$|Struct field value named X|
|(X.Y)$|Struct field value named X.Y|
|$|Shorthand for (X)$, omit (X) to indicate current struct field value|
|(X)$['A']|Map value with key A or struct A sub-field in the struct field X|
|(X)$[0]|The 0th element or sub-field of the struct field X(type: map, slice, array, struct)|
|len((X)$)|Built-in function len, the length of struct field X|
|mblen((X)$)|the length of string field X (character number)|
|regexp('^\\w*$', (X)$)|Regular match the struct field X, return boolean|
|regexp('^\\w*$')|Regular match the current struct field, return boolean|
|sprintf('X value: %v', (X)$)|fmt.Sprintf, format the value of struct field X|
|range(KvExpr, forEachExpr)|Iterate over an array, slice, or dictionary <br> - #k is the element key var <br> - #v is the element value var <br> - ## is the number of elements <br> - e.g. example|
|in((X)$, enum_1, ...enum_n)|Check if the first parameter is one of the enumerated parameters|
Operator priority(high -> low):
()!boolfloat64stringnil*/%+-<<=>>===!=&&||
Field Selector
field_lv1.field_lv2...field_lvn
Expression Selector
- If expression is single model or exprName is
@:
field_lv1.field_lv2...field_lvn
- If expression is multiple model and exprName is not
@:
field_lv1.field_lv2...field_lvn@exprName
Benchmark
goos: darwin
goarch: amd64
pkg: github.com/bytedance/go-tagexpr
BenchmarkTagExpr-4 10000000 148 ns/op 32 B/op 3 allocs/op
BenchmarkReflect-4 10000000 182 ns/op 16 B/op 2 allocs/op
PASS
Related Skills
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
commit-push-pr
140.6kCommit, push, and open a PR
