Format-Specific Type Handling
Censor supports different output formats (TEXT and JSON), which can sometimes handle the same Go types differently. This guide explains the format-specific behavior of Censor.
TEXT vs JSON Output
The two main formats supported by Censor are:
- TEXT: Human-readable format, similar to how Go prints values with 
fmt.Print() - JSON: Structured format suitable for APIs, logs, and data interchange
 
Type-Specific Behavior
Strings
Strings are processed similarly in both formats, but with different outputs:
// Create a Censor instance with TEXT format.
cText := censor.New(censor.WithFormatter(censor.FormatterText))
// Create a Censor instance with JSON format.
cJSON := censor.New(censor.WithFormatter(censor.FormatterJSON))
// Process a string.
email := "user@example.com"
// TEXT output: [CENSORED].
// JSON output: "[CENSORED]".  // Note the quotes for JSON strings.
Maps
Maps are represented differently in TEXT and JSON:
// Map with string keys and values.
user := map[string]string{
    "id":       "123",
    "email":    "user@example.com",
    "password": "secret123",
}
// TEXT output: map[id:123 email:[CENSORED] password:[CENSORED]].
// JSON output: {"id":"123","email":"[CENSORED]","password":"[CENSORED]"}.
Maps with non-string keys are handled differently:
// Map with integer keys.
scores := map[int]int{
    1: 100,
    2: 200,
    3: 300,
}
// TEXT output: map[1:100 2:200 3:300].
// JSON output: {"1":100,"2":200,"3":300}.  // Keys are converted to strings in JSON.
Structs
Structs are displayed differently in TEXT and JSON:
type User struct {
    ID        string `json:"id" censor:"display"`
    Email     string `json:"email"`
    Password  string `json:"password"`
}
user := User{
    ID:       "123",
    Email:    "user@example.com",
    Password: "secret123",
}
// TEXT output: {ID:123 Email:[CENSORED] Password:[CENSORED]}.
// JSON output: {"id":"123","email":"[CENSORED]","password":"[CENSORED]"}.
Note how JSON respects the json struct tags, while TEXT uses the field names.
Time
Time values are formatted according to the configuration:
createdAt := time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC)
// Default format.
// TEXT output: 2023-01-01T12:00:00Z.
// JSON output: "2023-01-01T12:00:00Z".  // JSON wraps the time string in quotes.
// With custom time format.
c := censor.New(censor.WithTimeFormat("2006-01-02"))
// TEXT output: 2023-01-01.
// JSON output: "2023-01-01".
Slices and Arrays
Slices and arrays are formatted differently:
// Slice of strings.
emails := []string{
    "user1@example.com",
    "user2@example.com",
    "user3@example.com",
}
// TEXT output: [[CENSORED] [CENSORED] [CENSORED]].
// JSON output: ["[CENSORED]","[CENSORED]","[CENSORED]"].
// Slice of integers.
scores := []int{100, 200, 300}
// TEXT output: [100 200 300].
// JSON output: [100,200,300].
Pointers
Pointers are dereferenced in both formats:
email := "user@example.com"
emailPtr := &email
// TEXT output: [CENSORED].
// JSON output: "[CENSORED]".
// Nil pointers.
var nilPtr *string
// TEXT output: <nil>.
// JSON output: null.
nil Values
Nil values are handled appropriately for each format:
// Nil interface.
var data interface{}
// TEXT output: <nil>.
// JSON output: null.
// Nil slice.
var slice []string
// TEXT output: [].
// JSON output: [].
JSON Struct Tags
For JSON output, Censor respects the json struct tags:
type User struct {
    ID           string `json:"id" censor:"display"`
    Email        string `json:"email"`
    Password     string `json:"password"`
    InternalNote string `json:"-"`  // Will be omitted in JSON output
    APIKey       string `json:"api_key,omitempty"`  // Will be omitted in JSON if empty
}
user := User{
    ID:       "123",
    Email:    "user@example.com",
    Password: "secret123",
    InternalNote: "This is an internal note",
    // APIKey is empty, so it will be omitted with omitempty.
}
// TEXT output: {ID:123 Email:[CENSORED] Password:[CENSORED] InternalNote:[CENSORED] APIKey:}.
// JSON output: {"id":"123","email":"[CENSORED]","password":"[CENSORED]"}.
Changing Format at Runtime
You can change the output format at runtime:
package main
import (
    "fmt"
    "github.com/vpakhuchyi/censor"
)
func main() {
    // Create a Censor instance with TEXT format.
    c := censor.New(censor.WithFormatter(censor.FormatterText))
    type User struct {
        ID       string `json:"id" censor:"display"`
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    user := User{
        ID:       "123",
        Email:    "user@example.com",
        Password: "secret123",
    }
    // Process with TEXT format.
    textOutput := c.Process(user)
    fmt.Println("TEXT output:", textOutput)
    // TEXT output: {ID:123 Email:[CENSORED] Password:[CENSORED]}.
    // Change to JSON format.
    c = censor.New(censor.WithFormatter(censor.FormatterJSON))
    
    // Process with JSON format.
    jsonOutput := c.Process(user)
    fmt.Println("JSON output:", jsonOutput)
    // JSON output: {"id":"123","email":"[CENSORED]","password":"[CENSORED]"}.
}
Complete Example
Here's a complete example showing format-specific behavior:
package main
import (
    "fmt"
    "time"
    "github.com/vpakhuchyi/censor"
)
type Address struct {
    Street  string `json:"street"`
    City    string `json:"city"`
    ZipCode string `json:"zip_code"`
}
type User struct {
    ID        string    `json:"id" censor:"display"`
    Email     string    `json:"email"`
    Password  string    `json:"password"`
    CreatedAt time.Time `json:"created_at"`
    Address   Address   `json:"address"`
    Tags      []string  `json:"tags"`
    Metadata  map[string]string `json:"metadata"`
}
func main() {
    // Create users
    user := User{
        ID:        "123",
        Email:     "user@example.com",
        Password:  "secret123",
        CreatedAt: time.Date(2023, 1, 1, 12, 0, 0, 0, time.UTC),
        Address: Address{
            Street:  "123 Main St",
            City:    "New York",
            ZipCode: "10001",
        },
        Tags: []string{"premium", "active"},
        Metadata: map[string]string{
            "last_login": "2023-01-01",
            "api_key":    "sk_live_123456789",
        },
    }
    // TEXT format
    cText := censor.New(censor.WithFormatter(censor.FormatterText))
    textOutput := cText.Process(user)
    fmt.Println("TEXT output:", textOutput)
    // TEXT output: {ID:123 Email:[CENSORED] Password:[CENSORED] CreatedAt:2023-01-01 12:00:00 +0000 UTC Address:{Street:[CENSORED] City:[CENSORED] ZipCode:[CENSORED]} Tags:[[CENSORED] [CENSORED]] Metadata:map[api_key:[CENSORED] last_login:[CENSORED]]}.
    // JSON format
    cJSON := censor.New(censor.WithFormatter(censor.FormatterJSON))
    jsonOutput := cJSON.Process(user)
    fmt.Println("JSON output:", jsonOutput)
    // JSON output: {"id":"123","email":"[CENSORED]","password":"[CENSORED]","created_at":"2023-01-01T12:00:00Z","address":{"street":"[CENSORED]","city":"[CENSORED]","zip_code":"[CENSORED]"},"tags":["[CENSORED]","[CENSORED]"],"metadata":{"api_key":"[CENSORED]","last_login":"[CENSORED]"}}.
}
Next Steps
- Learn about Basic Types
 - Check out Complex Types
 - See Special Types