Complex Types
Censor provides robust support for complex Go types. This guide details how each complex type is handled by Censor.
Structs
Structs are processed recursively, with each field being processed according to its type.
type Address struct {
Street string
City string
ZipCode string
}
type User struct {
ID string `censor:"display"`
Email string
Address Address
}
user := User{
ID: "123",
Email: "user@example.com",
Address: Address{
Street: "123 Main St",
City: "New York",
ZipCode: "10001",
},
}
// TEXT output: {ID:123 Email:[CENSORED] Address:{Street:[CENSORED] City:[CENSORED] ZipCode:[CENSORED]}}.
// JSON output: {"ID":"123","Email":"[CENSORED]","Address":{"Street":"[CENSORED]","City":"[CENSORED]","ZipCode":"[CENSORED]"}}.
Nested Structs
Censor processes nested structs to any depth:
type Contact struct {
Email string
Phone string
}
type Address struct {
Street string
City string
ZipCode string
}
type User struct {
ID string `censor:"display"`
Contact Contact
Address Address
}
user := User{
ID: "123",
Contact: Contact{
Email: "user@example.com",
Phone: "555-1234",
},
Address: Address{
Street: "123 Main St",
City: "New York",
ZipCode: "10001",
},
}
// TEXT output: {ID:123 Contact:{Email:[CENSORED] Phone:[CENSORED]} Address:{Street:[CENSORED] City:[CENSORED] ZipCode:[CENSORED]}}.
// JSON output: {"ID":"123","Contact":{"Email":"[CENSORED]","Phone":"[CENSORED]"},"Address":{"Street":"[CENSORED]","City":"[CENSORED]","ZipCode":"[CENSORED]"}}.
Embedded Structs
Censor handles embedded structs, processing all fields:
type Person struct {
Name string
Email string
}
type Employee struct {
Person
ID string `censor:"display"`
Title string
}
employee := Employee{
Person: Person{
Name: "John Doe",
Email: "john@example.com",
},
ID: "emp-123",
Title: "Software Engineer",
}
// TEXT output: {Person:{Name:[CENSORED] Email:[CENSORED]} ID:emp-123 Title:[CENSORED]}.
// JSON output: {"Name":"[CENSORED]","Email":"[CENSORED]","ID":"emp-123","Title":"[CENSORED]"}.
Maps
Maps are processed recursively, with both keys and values being processed according to their types.
Basic Maps
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]"}.
You can configure the map key handling:
// Create a Censor instance with custom key handling.
// Return true to display the key, false to mask it.
// Processing with custom map key handler.
c := censor.New(
censor.WithMapKeyHandler(func(key string) bool {
// Return true to display the key, false to mask it.
return key == "id"
}),
)
user := map[string]string{
"id": "123",
"email": "user@example.com",
"password": "secret123",
}
// Processing with custom map key handler.
masked := c.Process(user)
// TEXT output: map[id:123 [CENSORED]:[CENSORED] [CENSORED]:[CENSORED]].
// JSON output: {"id":"123","[CENSORED]":"[CENSORED]","[CENSORED]":"[CENSORED]"}.
Nested Maps
Censor processes nested maps to any depth:
userData := map[string]interface{}{
"id": "123",
"contact": map[string]string{
"email": "user@example.com",
"phone": "555-1234",
},
"address": map[string]string{
"street": "123 Main St",
"city": "New York",
"zipCode": "10001",
},
}
// TEXT output: map[id:123 contact:map[email:[CENSORED] phone:[CENSORED]] address:map[street:[CENSORED] city:[CENSORED] zipCode:[CENSORED]]].
// JSON output: {"id":"123","contact":{"email":"[CENSORED]","phone":"[CENSORED]"},"address":{"street":"[CENSORED]","city":"[CENSORED]","zipCode":"[CENSORED]"}}.
Maps with Complex Keys
Censor handles maps with non-string keys:
// Map with integer keys.
userScores := 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}.
// Map with struct keys.
type UserID struct {
ID string
Type string
}
userMap := map[UserID]string{
{ID: "123", Type: "customer"}: "John",
{ID: "456", Type: "employee"}: "Jane",
}
// TEXT output: map[{ID:[CENSORED] Type:[CENSORED]}:[CENSORED] {ID:[CENSORED] Type:[CENSORED]}:[CENSORED]].
// JSON output: {"{\"ID\":\"[CENSORED]\",\"Type\":\"[CENSORED]\"}":"[CENSORED]","{\"ID\":\"[CENSORED]\",\"Type\":\"[CENSORED]\"}":"[CENSORED]"}.
Slices and Arrays
Slices and arrays are processed by iterating over each element and processing it according to its type.
Basic Slices
// 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].
Slices of Structs
type User struct {
ID string `censor:"display"`
Email string
}
users := []User{
{ID: "1", Email: "user1@example.com"},
{ID: "2", Email: "user2@example.com"},
{ID: "3", Email: "user3@example.com"},
}
// TEXT output: [{ID:1 Email:[CENSORED]} {ID:2 Email:[CENSORED]} {ID:3 Email:[CENSORED]}].
// JSON output: [{"ID":"1","Email":"[CENSORED]"},{"ID":"2","Email":"[CENSORED]"},{"ID":"3","Email":"[CENSORED]"}].
Multidimensional Slices
// 2D slice of strings.
matrix := [][]string{
{"user1@example.com", "password1"},
{"user2@example.com", "password2"},
{"user3@example.com", "password3"},
}
// TEXT output: [[CENSORED] [CENSORED]] [[CENSORED] [CENSORED]] [[CENSORED] [CENSORED]]
// JSON output: [["[CENSORED]","[CENSORED]"],["[CENSORED]","[CENSORED]"],["[CENSORED]","[CENSORED]"]]
Arrays
Arrays are processed the same way as slices:
// Array of strings
var emails [3]string
emails[0] = "user1@example.com"
emails[1] = "user2@example.com"
emails[2] = "user3@example.com"
// TEXT output: [[CENSORED] [CENSORED] [CENSORED]]
// JSON output: ["[CENSORED]","[CENSORED]","[CENSORED]"]
Complete Example
Here's a complete example showing how Censor handles various complex types:
package main
import (
"fmt"
"github.com/vpakhuchyi/censor"
)
func main() {
// Create a Censor instance
c := censor.New()
// Define types
type Address struct {
Street string
City string
ZipCode string
}
type Contact struct {
Email string
Phone string
}
type User struct {
ID string `censor:"display"`
Name string
Contact Contact
Address Address
Tags []string
Metadata map[string]string
}
// Create user data
user := User{
ID: "123",
Name: "John Doe",
Contact: Contact{
Email: "john@example.com",
Phone: "555-1234",
},
Address: Address{
Street: "123 Main St",
City: "New York",
ZipCode: "10001",
},
Tags: []string{"customer", "premium", "active"},
Metadata: map[string]string{
"last_login": "2023-01-01",
"api_key": "sk_live_123456789",
},
}
// Process the data
masked := c.Process(user)
fmt.Printf("%+v\n", masked)
// TEXT output: {ID:123 Name:[CENSORED] Contact:{Email:[CENSORED] Phone:[CENSORED]} Address:{Street:[CENSORED] City:[CENSORED] ZipCode:[CENSORED]} Tags:[[CENSORED] [CENSORED] [CENSORED]] Metadata:map[api_key:[CENSORED] last_login:[CENSORED]]}
// JSON output: {"ID":"123","Name":"[CENSORED]","Contact":{"Email":"[CENSORED]","Phone":"[CENSORED]"},"Address":{"Street":"[CENSORED]","City":"[CENSORED]","ZipCode":"[CENSORED]"},"Tags":["[CENSORED]","[CENSORED]","[CENSORED]"],"Metadata":{"api_key":"[CENSORED]","last_login":"[CENSORED]"}}
}
Next Steps
- Learn about Basic Types
- Check out Special Types
- See Format-Specific handling