Skip to main content

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:

  1. TEXT: Human-readable format, similar to how Go prints values with fmt.Print()
  2. 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

  1. Learn about Basic Types
  2. Check out Complex Types
  3. See Special Types