Skip to content

json_en.md

maoxiaoyue edited this page May 14, 2026 · 1 revision

JSON Package (pkg/json)

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.

Key Features

  • Powerful Struct Validation: Integrates validator within ValidatedUnmarshal, 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 TypedUnmarshal for automatic type-safe conversion to match the target struct's fields.
  • Unified Error Format: Automatically organizes validator.ValidationErrors into structured ValidationError, making it easy for APIs to return clear error messages to the frontend.
  • Custom Validation: Provides RegisterValidation for 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.

Validator

Struct Validation Parsing

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)
    }
}

TypedUnmarshal

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.

Validation Error Format

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

Custom Validation Rules

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"`
}

JSON Schema Validation

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)

Marshal / MarshalCompact

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

convert.go provides bidirectional map ↔ JSON conversion with five functions. A nil map always returns "null"; empty input returns an error.

Map → JSON

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")

JSON → Map

// 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).

Full Conversion Flow Example

// 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

input.go provides input format validation and XSS protection sanitization.

ValidateEmail

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

ValidatePhone

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

SanitizeInput

Converts HTML special characters to HTML entities to prevent XSS injection:

raw := `<script>alert("xss")</script>`
safe := hypjson.SanitizeInput(raw)
// -> &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;

Conversion table:

Character Converted To
< &lt;
> &gt;
" &quot;
' &#x27;

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)

Quick Reference

Validator

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)

Convert

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

Input

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)

File Structure

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

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally