-
Notifications
You must be signed in to change notification settings - Fork 0
json_en.md
The json package provides advanced JSON processing capabilities for HypGo. Built on encoding/json, it integrates go-playground/validator/v10 for data validation during serialization and deserialization, and provides map/JSON conversion utilities and input sanitization functions.
-
Powerful Struct Validation: Integrates
validatorwithinValidatedUnmarshal, completing validation during parsing. -
JSON Schema Support: Provides JSON Schema-like property validation (
ValidateWithSchema), including required fields, length, regular expressions, and enum restrictions. -
Typed Unmarshal: Provides
TypedUnmarshalfor automatic type-safe conversion to match the target struct's fields. -
Unified Error Format: Automatically organizes
validator.ValidationErrorsinto structuredValidationError, making it easy for APIs to return clear error messages to the frontend. -
Custom Validation: Provides
RegisterValidationfor custom tags, allowing developers to easily extend validation logic. - Convert Utilities: Bidirectional map ↔ JSON conversion (string, bytes, formatted indent).
- Input Utilities: Built-in Email / Phone format validation and HTML input sanitization.
Create a Validator to validate required fields, email format, and more via struct tags (validate:"...") during JSON parsing:
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("Field %s error: %s\n", e.Field, e.Message) } }
Parses JSON with automatic type conversion based on the target struct's field types. Returns a clear error if types are incompatible:
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
The difference from ValidatedUnmarshal: TypedUnmarshal does not execute validate tag checks — it focuses solely on type conversion safety.
FormatErrors converts validator.ValidationErrors into a structured []ValidationError, where each error contains the field name, validation rule, original value, and a human-readable message:
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) }
Built-in message mappings:
| Tag | Message Format |
|---|---|
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} |
| others | {field} failed {tag} validation |
v := hypjson.NewValidator() // Custom "taiwan_phone" validation rule 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"` }
Apply a Schema definition to dynamic or non-Go-struct-defined JSON data:
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) }
Supported Property fields:
| Field | Type | Description |
|---|---|---|
Type |
string |
"string", "number", "integer", "boolean", "array", "object"
|
MinLength |
*int |
Minimum string length |
MaxLength |
*int |
Maximum string length |
Minimum |
*float64 |
Numeric lower bound |
Maximum |
*float64 |
Numeric upper bound |
Pattern |
string |
Regular expression |
Enum |
[]string |
Allowed values |
Format |
string |
Format hint (documentation only, no validation) |
type Response struct { ID int `json:"id"` Name string `json:"name"` } resp := Response{ID: 1, Name: "Alice"} // With indentation (human-readable) b, _ := hypjson.Marshal(resp) fmt.Println(string(b)) // { // "id": 1, // "name": "Alice" // } // Compact format (for transport) b, _ = hypjson.MarshalCompact(resp) fmt.Println(string(b)) // {"id":1,"name":"Alice"}
convert.go provides bidirectional map ↔ JSON conversion with five functions. A nil map always returns "null"; empty input returns an error.
m := map[string]interface{}{ "name": "Alice", "age": 30, "tags": []string{"admin", "user"}, } // Compact string s, err := hypjson.Map2JSON(m) // -> {"age":30,"name":"Alice","tags":["admin","user"]} // Formatted string (prefix="", indent=" ") s, err = hypjson.Map2JSONIndent(m, "", " ") // -> { // "age": 30, // "name": "Alice", // "tags": ["admin","user"] // } // Compact bytes (for direct use in HTTP responses) b, err := hypjson.Map2JSONBytes(m)
Nil map behavior:
s, _ := hypjson.Map2JSON(nil) // -> "null" b, _ := hypjson.Map2JSONBytes(nil) // -> []byte("null")
// Parse from string m, err := hypjson.JSON2Map(`{"name":"Alice","age":30}`) // m["name"] == "Alice", m["age"] == float64(30) // Parse from bytes b := []byte(`{"key":"value"}`) m, err = hypjson.JSON2MapBytes(b)
Note: numeric types are uniformly float64 after JSON parsing (standard encoding/json behavior).
// Scenario: receive JSON request -> modify field -> return 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 provides input format validation and XSS protection sanitization.
Validates email format using a regular expression (^[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
Validates phone numbers in E.164 format (^\+?[1-9]\d{1,14}$):
hypjson.ValidatePhone("+886912345678") // -> true hypjson.ValidatePhone("0912345678") // -> false (E.164 format required) hypjson.ValidatePhone("+1-800-555") // -> false
Converts HTML special characters to HTML entities to prevent XSS injection:
raw := `<script>alert("xss")</script>` safe := hypjson.SanitizeInput(raw) // -> <script>alert("xss")</script>
Conversion table:
| Character | Converted To |
|---|---|
< |
< |
> |
> |
" |
" |
' |
' |
Combined usage example (sanitize JSON input before storing):
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 } // Sanitize user input before storing to database req.Content = hypjson.SanitizeInput(req.Content)
| Function / Method | Description |
|---|---|
NewValidator() |
Create a Validator (auto uses json tag as field name) |
v.ValidatedUnmarshal(data, dest) |
Parse JSON and run validate tag checks |
TypedUnmarshal(data, dest) |
Parse JSON with type-safe conversion (no validation) |
ValidateWithSchema(data, schema) |
Validate raw JSON against a Schema definition |
v.FormatErrors(err) |
Convert validation errors to []ValidationError
|
v.RegisterValidation(tag, fn) |
Register a custom validation rule |
Marshal(v) |
Serialize with indentation |
MarshalCompact(v) |
Serialize compact (no indentation) |
| Function | Description |
|---|---|
Map2JSON(m) |
map → JSON string (compact) |
Map2JSONIndent(m, prefix, indent) |
map → JSON string (formatted) |
Map2JSONBytes(m) |
map → JSON bytes (compact) |
JSON2Map(s) |
JSON string → map |
JSON2MapBytes(b) |
JSON bytes → map |
| Function | Description |
|---|---|
ValidateEmail(email) |
Validate email format, returns bool
|
ValidatePhone(phone) |
Validate E.164 phone format, returns bool
|
SanitizeInput(input) |
Escape HTML special characters (XSS protection) |
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