-
Notifications
You must be signed in to change notification settings - Fork 0
json.md
json 套件為 HypGo 提供進階 JSON 處理能力,基於 encoding/json 整合 go-playground/validator/v10,在序列化或反序列化過程中進行資料檢查與驗證,並提供 map/JSON 互轉工具及輸入清理函式。
-
強大的 Struct 驗證: 將
validator內建於ValidatedUnmarshal中,解析即完成校驗。 -
JSON Schema 驗證: 提供像 JSON Schema 一樣的屬性驗證功能 (
ValidateWithSchema),包括必填欄位、長度、正則表達式或 Enum 限定。 -
類型自動轉型: 提供
TypedUnmarshal自動適配目標結構體支援的類型轉換操作。 -
統一錯誤格式: 將
validator.ValidationErrors自動整理為結構化的ValidationError,方便 API 將明確的錯誤訊息回傳給前端。 -
自定義驗證: 提供
RegisterValidation讓開發者擴充驗證邏輯。 - Convert 工具: 提供 map ↔ JSON 雙向轉換(字串、bytes、格式化縮排)。
- Input 工具: 內建 Email / Phone 格式驗證與 HTML 輸入清理。
透過建立一個 Validator,在 json.Unmarshal 的當下利用 struct tag (validate:"...") 驗證必填項、Email 格式等:
import hypjson "github.com/maoxiaoyue/hypgo/pkg/json" type UserRequest struct { Name string `json:"name" validate:"required"` Email string `json:"email" validate:"required,email"` Age int `json:"age" validate:"min=18"` } v := hypjson.NewValidator() data := []byte(`{"name": "Alice", "email": "alice@example", "age": 16}`) var req UserRequest err := v.ValidatedUnmarshal(data, &req) if err != nil { errs := v.FormatErrors(err) for _, e := range errs { fmt.Printf("欄位 %s 發生錯誤: %s\n", e.Field, e.Message) } }
在解析過程中根據目標 struct 的欄位型別進行自動轉型,若型別不相容則回傳明確錯誤:
type Config struct { Port int `json:"port"` Timeout float64 `json:"timeout"` Debug bool `json:"debug"` } data := []byte(`{"port": 8080, "timeout": 30.5, "debug": true}`) var cfg Config if err := hypjson.TypedUnmarshal(data, &cfg); err != nil { log.Fatal(err) } // cfg.Port = 8080, cfg.Timeout = 30.5, cfg.Debug = true
與 ValidatedUnmarshal 的差異:TypedUnmarshal 不執行 validate tag 驗證,專注於型別轉換的安全性。
FormatErrors 將 validator.ValidationErrors 轉換為結構化的 []ValidationError,每個錯誤包含欄位名稱、驗證規則、原始值與可讀訊息:
type ValidationError struct { Field string Tag string Value interface{} Message string } errs := v.FormatErrors(err) for _, e := range errs { // e.Field → "email" // e.Tag → "email" // e.Message → "email must be a valid email address" fmt.Printf("[%s] %s\n", e.Field, e.Message) }
內建的訊息對應:
| Tag | 訊息格式 |
|---|---|
required |
{field} is required |
email |
{field} must be a valid email address |
min |
{field} must be at least {param} |
max |
{field} must not exceed {param} |
| 其他 | {field} failed {tag} validation |
v := hypjson.NewValidator() // 自定義 "taiwan_phone" 驗證規則 v.RegisterValidation("taiwan_phone", func(fl validator.FieldLevel) bool { phone := fl.Field().String() return strings.HasPrefix(phone, "09") && len(phone) == 10 }) type ContactForm struct { Phone string `json:"phone" validate:"required,taiwan_phone"` }
對動態或未定義成 Go struct 的 JSON 資料套用 Schema 定義:
minLen := 3 maxLen := 20 schema := hypjson.Schema{ Type: "object", Required: []string{"username"}, Properties: map[string]hypjson.Property{ "username": { Type: "string", MinLength: &minLen, MaxLength: &maxLen, }, "age": { Type: "number", Minimum: func() *float64 { v := 0.0; return &v }(), Maximum: func() *float64 { v := 150.0; return &v }(), }, "status": { Type: "string", Enum: []string{"active", "inactive", "pending"}, }, "email": { Type: "string", Pattern: `^[^@]+@[^@]+\.[^@]+$`, }, }, } data := []byte(`{"username": "Al", "status": "unknown"}`) if err := hypjson.ValidateWithSchema(data, schema); err != nil { log.Println("Schema validation failed:", err) }
Property 支援的欄位:
| 欄位 | 型別 | 說明 |
|---|---|---|
Type |
string |
"string", "number", "integer", "boolean", "array", "object"
|
MinLength |
*int |
字串最小長度 |
MaxLength |
*int |
字串最大長度 |
Minimum |
*float64 |
數值下限 |
Maximum |
*float64 |
數值上限 |
Pattern |
string |
正則表達式 |
Enum |
[]string |
允許值清單 |
Format |
string |
格式提示(文件用,不做驗證) |
type Response struct { ID int `json:"id"` Name string `json:"name"` } resp := Response{ID: 1, Name: "Alice"} // 帶縮排(人類可讀) b, _ := hypjson.Marshal(resp) fmt.Println(string(b)) // { // "id": 1, // "name": "Alice" // } // 緊湊格式(傳輸用) b, _ = hypjson.MarshalCompact(resp) fmt.Println(string(b)) // {"id":1,"name":"Alice"}
convert.go 提供 map ↔ JSON 雙向轉換,共五個函式。nil map 一律回傳 "null",空輸入回傳錯誤。
m := map[string]interface{}{ "name": "Alice", "age": 30, "tags": []string{"admin", "user"}, } // 緊湊字串 s, err := hypjson.Map2JSON(m) // → {"age":30,"name":"Alice","tags":["admin","user"]} // 格式化字串(prefix="", indent=" ") s, err = hypjson.Map2JSONIndent(m, "", " ") // → { // "age": 30, // "name": "Alice", // "tags": ["admin","user"] // } // 緊湊 bytes(直接用於 HTTP response) b, err := hypjson.Map2JSONBytes(m)
nil map 的行為:
s, _ := hypjson.Map2JSON(nil) // → "null" b, _ := hypjson.Map2JSONBytes(nil) // → []byte("null")
// 從字串解析 m, err := hypjson.JSON2Map(`{"name":"Alice","age":30}`) // m["name"] == "Alice", m["age"] == float64(30) // 從 bytes 解析 b := []byte(`{"key":"value"}`) m, err = hypjson.JSON2MapBytes(b)
注意:數值型別在 JSON 解析後統一為 float64(標準 encoding/json 行為)。
// 情境:接收 JSON 請求 → 修改欄位 → 回傳 JSON raw := []byte(`{"user":"alice","score":100}`) m, err := hypjson.JSON2MapBytes(raw) if err != nil { return err } m["score"] = m["score"].(float64) + 10 // score + 10 result, err := hypjson.Map2JSON(m) // → {"score":110,"user":"alice"}
input.go 提供輸入格式驗證與 XSS 防護清理。
使用正則表達式驗證 Email 格式(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$):
hypjson.ValidateEmail("alice@example.com") // → true hypjson.ValidateEmail("alice@example") // → false hypjson.ValidateEmail("not-an-email") // → false
使用 E.164 格式驗證電話號碼(^\+?[1-9]\d{1,14}$):
hypjson.ValidatePhone("+886912345678") // → true hypjson.ValidatePhone("0912345678") // → false(需 E.164 格式) hypjson.ValidatePhone("+1-800-555") // → false
將 HTML 特殊字元轉換為 HTML entities,防止 XSS 注入:
raw := `<script>alert("xss")</script>` safe := hypjson.SanitizeInput(raw) // → <script>alert("xss")</script>
轉換對照:
| 字元 | 轉換後 |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
結合使用範例(接收 JSON 輸入後清理):
type CommentRequest struct { Content string `json:"content" validate:"required,max=500"` } v := hypjson.NewValidator() var req CommentRequest if err := v.ValidatedUnmarshal(data, &req); err != nil { return err } // 清理使用者輸入,再存入資料庫 req.Content = hypjson.SanitizeInput(req.Content)
| 函式 / 方法 | 說明 |
|---|---|
NewValidator() |
建立 Validator(自動以 json tag 為欄位名) |
v.ValidatedUnmarshal(data, dest) |
解析 JSON 並執行 validate tag 驗證 |
TypedUnmarshal(data, dest) |
解析 JSON 並做型別安全轉換(無驗證) |
ValidateWithSchema(data, schema) |
用 Schema 定義驗證原始 JSON |
v.FormatErrors(err) |
將驗證錯誤轉為 []ValidationError
|
v.RegisterValidation(tag, fn) |
註冊自定義驗證規則 |
Marshal(v) |
序列化(帶縮排) |
MarshalCompact(v) |
序列化(緊湊,無縮排) |
| 函式 | 說明 |
|---|---|
Map2JSON(m) |
map → JSON 字串(緊湊) |
Map2JSONIndent(m, prefix, indent) |
map → JSON 字串(格式化) |
Map2JSONBytes(m) |
map → JSON bytes(緊湊) |
JSON2Map(s) |
JSON 字串 → map |
JSON2MapBytes(b) |
JSON bytes → map |
| 函式 | 說明 |
|---|---|
ValidateEmail(email) |
驗證 Email 格式,回傳 bool
|
ValidatePhone(phone) |
驗證 E.164 電話格式,回傳 bool
|
SanitizeInput(input) |
HTML 特殊字元轉義(XSS 防護) |
pkg/json/
├── validator.go # Validator, ValidatedUnmarshal, TypedUnmarshal,
│ # ValidateWithSchema, Marshal, FormatErrors, RegisterValidation
├── convert.go # Map2JSON, Map2JSONIndent, Map2JSONBytes, JSON2Map, JSON2MapBytes
├── input.go # ValidateEmail, ValidatePhone, SanitizeInput
├── validator_test.go
└── convert_test.go
設計文件
套件
- config — 設定
- context — 請求上下文
- router — 路由器
- server — 伺服器
- middleware — 中介層
- websocket — WebSocket
- hidb — 資料庫 ORM
- hidb/cassandra — Cassandra
- logger — 日誌
- json — JSON 處理
- grpc — gRPC
AI 協作工具鏈
- schema — Schema-first 路由
- manifest — 專案 Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — 智慧 Scaffold
- airules — AI Rules
CLI 命令
- hyp 總覽
- hyp new
- hyp api
- hyp run
- hyp restart
- hyp generate
- hyp migrate
- hyp context
- hyp ai-rules
- hyp chkcomment
- hyp impact
- hyp docker
- hyp health
- hyp version
- hyp difflog
Design Docs
Packages
- config — Configuration
- context — Request Context
- router — Router
- server — Server
- middleware — Middleware
- websocket — WebSocket
- hidb — Database ORM
- hidb/cassandra - Cassandra 5.0
- logger — Logger
- json — JSON
- grpc — gRPC
AI Collaboration Toolchain
- schema — Schema-first Routing
- manifest — Project Manifest
- contract — Contract Testing
- errors — Typed Error Catalog
- migrate — Migration Diff
- scaffold — Smart Scaffold
- airules — AI Rules
CLI Commands