-
Notifications
You must be signed in to change notification settings - Fork 0
FxJSON use guidelines (English)
CloudZA edited this page Aug 18, 2025
·
3 revisions
- Introduction
- Installation & Setup
- Core Concepts
- Performance Optimization
- Advanced Features
- Best Practices
- Error Handling
- Troubleshooting
- Migration Guide
- API Reference
FxJSON is a high-performance JSON parsing library for Go that focuses on speed, memory efficiency, and ease of use. It provides significant performance improvements over the standard library while maintaining memory safety and offering a rich set of features.
- 🔥 High Performance: 20x-67x faster traversal operations
- ⚡ Memory Efficient: Zero-allocation core operations
- 🛡️ Memory Safety: Complete boundary checking
- 🎯 Easy to Use: Intuitive chainable API
- 🌐 Unicode Support: Perfect support for Chinese, emoji, and Unicode characters
- 🧩 Nested JSON Expansion: Automatic recognition and expansion of nested JSON strings
- 🔍 Advanced Querying: SQL-style conditional queries and filtering
- 📊 Data Aggregation: Built-in statistical and aggregation functions
- 💾 Smart Caching: High-performance caching with LRU eviction
- 🔧 Debug Tools: Enhanced debugging and analysis features
| Operation | FxJSON | Standard Library | Performance Gain | Memory Advantage |
|---|---|---|---|---|
| ForEach Traversal | 104.7 ns | 2115 ns | 20.2x | Zero allocations vs 57 allocs |
| Array Traversal | 30.27 ns | 2044 ns | 67.5x | Zero allocations vs 57 allocs |
| Deep Traversal | 1363 ns | 2787 ns | 2.0x | 29 allocs vs 83 allocs |
| Complex Traversal | 1269 ns | 3280 ns | 2.6x | Zero allocations vs 104 allocs |
// Traditional way with encoding/json
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
data := []byte(`{"name":"Alice","age":30}`)
var user User
json.Unmarshal(data, &user) // Slow, lots of memory allocations
// FxJSON way - much faster!
node := fxjson.FromBytes(data)
name := node.Get("name").StringOr("Unknown") // Clean and fast
age := node.Get("age").IntOr(0) // With default values!# Just one command to get started
go get github.com/icloudza/fxjsonpackage main
import (
"fmt"
"github.com/icloudza/fxjson"
)
func main() {
// Let's parse some JSON!
jsonData := []byte(`{
"message": "Hello, World!",
"timestamp": 1234567890,
"success": true
}`)
// Create a node from JSON bytes
node := fxjson.FromBytes(jsonData)
// Extract values - it's that simple!
message := node.Get("message").StringOr("No message")
timestamp := node.Get("timestamp").IntOr(0)
success := node.Get("success").BoolOr(false)
fmt.Printf("Message: %s at time %d (success: %v)\n",
message, timestamp, success)
}A Node is the core type in FxJSON. It represents any JSON value - object, array, string, number, boolean, or null.
// A Node can be anything in JSON
jsonData := []byte(`{
"string": "Hello",
"number": 42,
"boolean": true,
"null": null,
"array": [1, 2, 3],
"object": {"nested": "value"}
}`)
node := fxjson.FromBytes(jsonData)
// Each Get() returns another Node
stringNode := node.Get("string") // Node representing "Hello"
numberNode := node.Get("number") // Node representing 42
arrayNode := node.Get("array") // Node representing [1,2,3]
objectNode := node.Get("object") // Node representing {"nested":"value"}FxJSON provides two ways to extract values:
// Method 1: Traditional with error handling
value, err := node.Get("key").String()
if err != nil {
// Handle error
value = "default"
}
// Method 2: Modern with default values (recommended!)
value := node.Get("key").StringOr("default") // Clean, simple, safeNavigate complex JSON structures easily:
jsonData := []byte(`{
"user": {
"profile": {
"address": {
"city": "Beijing"
}
}
}
}`)
node := fxjson.FromBytes(jsonData)
// Instead of multiple Get() calls
city1 := node.Get("user").Get("profile").Get("address").Get("city").StringOr("")
// Use GetPath() for cleaner code
city2 := node.GetPath("user.profile.address.city").StringOr("")
// Access array elements in paths
jsonData2 := []byte(`{"users": [{"name": "Alice"}, {"name": "Bob"}]}`)
node2 := fxjson.FromBytes(jsonData2)
secondUser := node2.GetPath("users[1].name").StringOr("") // "Bob"// Sample data: user information
userJSON := []byte(`{
"id": 12345,
"username": "alice_wonder",
"email": "alice@example.com",
"profile": {
"firstName": "Alice",
"lastName": "Wonder",
"age": 28,
"interests": ["coding", "reading", "hiking"]
},
"settings": {
"theme": "dark",
"notifications": true,
"language": "en"
}
}`)
user := fxjson.FromBytes(userJSON)
// Basic field access
username := user.Get("username").StringOr("anonymous")
email := user.Get("email").StringOr("")
// Nested field access
firstName := user.Get("profile").Get("firstName").StringOr("")
// Or use path for cleaner code
lastName := user.GetPath("profile.lastName").StringOr("")
// Check if fields exist
if user.HasKey("email") {
fmt.Println("User has email")
}
// Get all keys from an object
settingsKeys := user.Get("settings").GetAllKeys()
// Result: ["theme", "notifications", "language"]
// Convert to Go map for easier manipulation
settingsMap := user.Get("settings").ToMap()
for key, value := range settingsMap {
fmt.Printf("%s: %v\n", key, value.Raw())
}// Sample data: shopping cart
cartJSON := []byte(`{
"items": [
{"id": 1, "name": "Laptop", "price": 999.99, "qty": 1},
{"id": 2, "name": "Mouse", "price": 29.99, "qty": 2},
{"id": 3, "name": "Keyboard", "price": 79.99, "qty": 1}
],
"discounts": [10, 15, 5],
"tags": ["electronics", "computers", "accessories"]
}`)
cart := fxjson.FromBytes(cartJSON)
items := cart.Get("items")
// Get array length
itemCount := items.Len() // 3
// Access by index
firstItem := items.Index(0)
firstItemName := firstItem.Get("name").StringOr("")
// Iterate through array - the fast way!
var totalPrice float64
items.ArrayForEach(func(index int, item fxjson.Node) bool {
price := item.Get("price").FloatOr(0)
qty := item.Get("qty").IntOr(1)
totalPrice += price * float64(qty)
fmt.Printf("Item %d: %s - $%.2f x %d\n",
index+1,
item.Get("name").StringOr(""),
price, qty)
return true // continue iteration
})
// Array operations
discounts := cart.Get("discounts")
firstDiscount := discounts.First() // First element
lastDiscount := discounts.Last() // Last element
topTwo := discounts.Slice(0, 2) // First two elements
// Convert to Go slice
if discountSlice, err := discounts.ToIntSlice(); err == nil {
maxDiscount := int64(0)
for _, d := range discountSlice {
if d > maxDiscount {
maxDiscount = d
}
}
fmt.Printf("Max discount: %d%%\n", maxDiscount)
}// Mixed type JSON
mixedJSON := []byte(`{
"string": "hello",
"integer": 42,
"float": 3.14,
"boolean": true,
"null": null,
"array": [1, 2, 3],
"object": {"key": "value"}
}`)
node := fxjson.FromBytes(mixedJSON)
// Check types before conversion
fields := []string{"string", "integer", "float", "boolean", "null", "array", "object"}
for _, field := range fields {
n := node.Get(field)
// Multiple ways to check type
switch {
case n.IsString():
fmt.Printf("%s is a string: %s\n", field, n.StringOr(""))
case n.IsNumber():
// Numbers can be int or float
if n.IsInteger() {
fmt.Printf("%s is an integer: %d\n", field, n.IntOr(0))
} else {
fmt.Printf("%s is a float: %.2f\n", field, n.FloatOr(0))
}
case n.IsBool():
fmt.Printf("%s is a boolean: %v\n", field, n.BoolOr(false))
case n.IsNull():
fmt.Printf("%s is null\n", field)
case n.IsArray():
fmt.Printf("%s is an array with %d items\n", field, n.Len())
case n.IsObject():
fmt.Printf("%s is an object with keys: %v\n", field, n.GetAllKeys())
}
}
// Safe type conversion with defaults
anyValue := node.Get("unknown_field")
safeString := anyValue.StringOr("default") // Won't panic
safeInt := anyValue.IntOr(0) // Safe with default
safeFloat := anyValue.FloatOr(0.0) // Always safe
safeBool := anyValue.BoolOr(false) // No errors!// User registration data
registrationJSON := []byte(`{
"email": "user@example.com",
"alternativeEmail": "invalid-email",
"website": "https://example.com",
"ipAddress": "192.168.1.100",
"phone": "+1234567890",
"userId": "550e8400-e29b-41d4-a716-446655440000"
}`)
reg := fxjson.FromBytes(registrationJSON)
// Email validation
if reg.Get("email").IsValidEmail() {
fmt.Println("✅ Primary email is valid")
}
if !reg.Get("alternativeEmail").IsValidEmail() {
fmt.Println("❌ Alternative email is invalid")
}
// URL validation
if reg.Get("website").IsValidURL() {
fmt.Println("✅ Website URL is valid")
}
// IP address validation
if reg.Get("ipAddress").IsValidIPv4() {
fmt.Println("✅ IPv4 address is valid")
}
// Phone validation (E.164 format)
if reg.Get("phone").IsValidPhone() {
fmt.Println("✅ Phone number is valid")
}
// UUID validation
if reg.Get("userId").IsValidUUID() {
fmt.Println("✅ User ID is a valid UUID")
}
// Custom validation logic
password := reg.Get("password").StringOr("")
if len(password) < 8 {
fmt.Println("❌ Password too short")
}// Order data with many fields
orderJSON := []byte(`{
"order": {
"id": "ORD-2024-001",
"customer": {
"id": "CUST-123",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"address": {
"street": "123 Main St",
"city": "New York",
"country": "USA"
}
},
"items": [
{"sku": "LAPTOP-01", "qty": 1, "price": 999.99},
{"sku": "MOUSE-02", "qty": 2, "price": 29.99}
],
"totals": {
"subtotal": 1059.97,
"tax": 95.40,
"shipping": 15.00,
"total": 1170.37
}
}
}`)
order := fxjson.FromBytes(orderJSON)
// Get multiple values at once - super efficient!
values := order.GetMultiple(
"order.id",
"order.customer.name",
"order.customer.email",
"order.totals.total",
)
// Extract all values with defaults
orderId := values[0].StringOr("N/A")
customerName := values[1].StringOr("Guest")
customerEmail := values[2].StringOr("")
totalAmount := values[3].FloatOr(0.0)
fmt.Printf("Order %s for %s (%.2f)\n", orderId, customerName, totalAmount)
// Check if all required fields exist
requiredFields := []string{
"order.id",
"order.customer.id",
"order.customer.email",
"order.items",
"order.totals.total",
}
if order.HasAllPaths(requiredFields...) {
fmt.Println("✅ All required fields present")
} else {
fmt.Println("❌ Missing required fields")
}
// Check if customer has any contact method
contactMethods := []string{
"order.customer.email",
"order.customer.phone",
"order.customer.mobile",
"order.customer.fax",
}
if order.HasAnyPath(contactMethods...) {
fmt.Println("✅ Customer has at least one contact method")
}// Product catalog
catalogJSON := []byte(`{
"products": [
{"id": 1, "name": "Laptop", "category": "Electronics", "price": 999, "inStock": true},
{"id": 2, "name": "Desk", "category": "Furniture", "price": 299, "inStock": true},
{"id": 3, "name": "Monitor", "category": "Electronics", "price": 399, "inStock": false},
{"id": 4, "name": "Chair", "category": "Furniture", "price": 199, "inStock": true},
{"id": 5, "name": "Keyboard", "category": "Electronics", "price": 79, "inStock": true}
]
}`)
catalog := fxjson.FromBytes(catalogJSON)
products := catalog.Get("products")
// Find first electronics product
_, firstElectronics, found := products.FindInArray(func(idx int, product fxjson.Node) bool {
return product.Get("category").StringOr("") == "Electronics"
})
if found {
name := firstElectronics.Get("name").StringOr("")
price := firstElectronics.Get("price").IntOr(0)
fmt.Printf("First electronics item: %s ($%d)\n", name, price)
}
// Filter all products in stock
inStockProducts := products.FilterArray(func(idx int, product fxjson.Node) bool {
return product.Get("inStock").BoolOr(false)
})
fmt.Printf("Products in stock: %d\n", len(inStockProducts))
for _, product := range inStockProducts {
fmt.Printf("- %s\n", product.Get("name").StringOr(""))
}
// Count products under $500
affordableCount := products.CountIf(func(idx int, product fxjson.Node) bool {
return product.Get("price").IntOr(0) < 500
})
fmt.Printf("Affordable products (< $500): %d\n", affordableCount)
// Check if all products have required fields
allValid := products.AllMatch(func(idx int, product fxjson.Node) bool {
return product.HasKey("id") &&
product.HasKey("name") &&
product.HasKey("price")
})
if allValid {
fmt.Println("✅ All products have required fields")
}
// Check if any product is out of stock
hasOutOfStock := products.AnyMatch(func(idx int, product fxjson.Node) bool {
return !product.Get("inStock").BoolOr(true)
})
if hasOutOfStock {
fmt.Println("⚠️ Some products are out of stock")
}// Complex nested structure
companyJSON := []byte(`{
"company": {
"name": "TechCorp",
"departments": [
{
"name": "Engineering",
"teams": [
{
"name": "Backend",
"members": [
{"name": "Alice", "role": "Lead"},
{"name": "Bob", "role": "Senior"}
]
},
{
"name": "Frontend",
"members": [
{"name": "Charlie", "role": "Senior"},
{"name": "Diana", "role": "Junior"}
]
}
]
},
{
"name": "Marketing",
"teams": [
{
"name": "Digital",
"members": [
{"name": "Eve", "role": "Manager"}
]
}
]
}
]
}
}`)
company := fxjson.FromBytes(companyJSON)
// Walk through entire JSON structure
fmt.Println("Company Structure:")
company.Walk(func(path string, node fxjson.Node) bool {
// Only print leaf nodes (actual values)
if node.IsScalar() {
indent := strings.Count(path, ".") + strings.Count(path, "[")
fmt.Printf("%s%s = %v\n",
strings.Repeat(" ", indent),
path,
node.Raw())
}
return true // continue walking
})
// Count all employees
employeeCount := 0
company.Walk(func(path string, node fxjson.Node) bool {
if strings.Contains(path, "members[") && strings.HasSuffix(path, ".name") {
employeeCount++
}
return true
})
fmt.Printf("\nTotal employees: %d\n", employeeCount)
// Find all team names
var teamNames []string
company.Walk(func(path string, node fxjson.Node) bool {
if strings.Contains(path, "teams[") && strings.HasSuffix(path, ".name") {
if name, err := node.String(); err == nil {
teamNames = append(teamNames, name)
}
}
return true
})
fmt.Printf("Teams: %v\n", teamNames)// Article metadata
articleJSON := []byte(`{
"title": " Introduction to Go Programming ",
"slug": "intro-to-go",
"description": "Learn the BASICS of Go programming language",
"tags": "golang,programming,tutorial",
"url": "https://example.com/articles/intro-to-go",
"filename": "intro_to_go.pdf"
}`)
article := fxjson.FromBytes(articleJSON)
// String manipulation
title := article.Get("title")
trimmedTitle, _ := title.Trim() // Remove whitespace
upperTitle, _ := title.ToUpper() // Convert to uppercase
lowerTitle, _ := title.ToLower() // Convert to lowercase
fmt.Printf("Original: '%s'\n", title.StringOr(""))
fmt.Printf("Trimmed: '%s'\n", trimmedTitle)
fmt.Printf("Upper: '%s'\n", upperTitle)
fmt.Printf("Lower: '%s'\n", lowerTitle)
// String checking
url := article.Get("url")
if url.Contains("example.com") {
fmt.Println("✅ URL is from example.com")
}
if url.StartsWith("https://") {
fmt.Println("✅ URL uses HTTPS")
}
filename := article.Get("filename")
if filename.EndsWith(".pdf") {
fmt.Println("✅ File is a PDF")
}
// Working with tags
tags := article.Get("tags").StringOr("")
tagList := strings.Split(tags, ",")
fmt.Printf("Tags: %v\n", tagList)// Configuration management
defaultConfigJSON := []byte(`{
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp",
"pool": 10
},
"cache": {
"enabled": true,
"ttl": 3600
},
"logging": {
"level": "info",
"format": "json"
}
}`)
userConfigJSON := []byte(`{
"database": {
"host": "db.production.com",
"port": 5432,
"user": "admin",
"password": "secret"
},
"cache": {
"ttl": 7200
}
}`)
defaultConfig := fxjson.FromBytes(defaultConfigJSON)
userConfig := fxjson.FromBytes(userConfigJSON)
// Merge configurations (user config overrides defaults)
dbDefaults := defaultConfig.Get("database")
dbUser := userConfig.Get("database")
finalDb := dbDefaults.Merge(dbUser)
// Pick only safe fields (exclude sensitive data)
safeDb := dbUser.Pick("host", "port", "name")
fmt.Println("Safe to log:", safeDb)
// Omit sensitive fields
publicDb := dbUser.Omit("password", "user")
fmt.Println("Public config:", publicDb)
// Complex merge scenario
baseSettings := fxjson.FromBytes([]byte(`{
"theme": "light",
"language": "en",
"notifications": {
"email": true,
"push": false,
"sms": false
}
}`))
userSettings := fxjson.FromBytes([]byte(`{
"theme": "dark",
"notifications": {
"push": true
}
}`))
// Merge preserves structure
finalSettings := baseSettings.Merge(userSettings)
// Result: theme="dark", language="en", notifications={email:true, push:true, sms:false}// Large dataset
largeJSON := []byte(`{
"users": [
{"id": 1, "name": "User1", "score": 85, "active": true},
{"id": 2, "name": "User2", "score": 92, "active": false},
// ... imagine thousands more ...
]
}`)
data := fxjson.FromBytes(largeJSON)
users := data.Get("users")
// DON'T do this - creates intermediate nodes
var totalScore int64
for i := 0; i < users.Len(); i++ {
user := users.Index(i) // Creates a new node each time
score := user.Get("score").IntOr(0)
totalScore += score
}
// DO this instead - zero allocations!
var betterTotalScore int64
users.ArrayForEach(func(index int, user fxjson.Node) bool {
betterTotalScore += user.Get("score").IntOr(0)
return true // Continue iteration
})
// For objects, use ForEach for maximum speed
userObj := users.Index(0)
userObj.ForEach(func(key string, value fxjson.Node) bool {
// Process each field with zero allocations
fmt.Printf("%s: %s\n", key, value.Raw())
return true
})type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Tags []string `json:"tags"`
Profile struct {
Bio string `json:"bio"`
Location string `json:"location"`
} `json:"profile"`
}
userJSON := []byte(`{
"id": 123,
"username": "johndoe",
"email": "john@example.com",
"tags": ["developer", "gopher"],
"profile": {
"bio": "Go enthusiast",
"location": "San Francisco"
}
}`)
// Good - Node-based decoding
node := fxjson.FromBytes(userJSON)
var user1 User
err := node.Decode(&user1)
// Better - Direct decoding
var user2 User
err = fxjson.DecodeStruct(userJSON, &user2)
// Best - Ultra-fast decoding
var user3 User
err = fxjson.DecodeStructFast(userJSON, &user3)
// Performance comparison:
// node.Decode() - 1x speed (baseline)
// DecodeStruct() - 1.5x speed
// DecodeStructFast() - 2x speedimport (
"time"
"github.com/icloudza/fxjson"
)
// Enable caching for frequently parsed JSON
cache := fxjson.NewMemoryCache(100) // Cache up to 100 items
fxjson.EnableCaching(cache)
// First parse - goes to cache
start := time.Now()
node1 := fxjson.FromBytesWithCache(userJSON, 5*time.Minute)
firstTime := time.Since(start)
// Second parse - from cache (much faster!)
start = time.Now()
node2 := fxjson.FromBytesWithCache(userJSON, 5*time.Minute)
cachedTime := time.Since(start)
fmt.Printf("First parse: %v\n", firstTime)
fmt.Printf("Cached parse: %v (%.1fx faster)\n",
cachedTime, float64(firstTime)/float64(cachedTime))
// Check cache statistics
stats := cache.Stats()
fmt.Printf("Cache hit rate: %.1f%%\n", stats.HitRate*100)
// Disable caching when done
fxjson.DisableCaching()// Typical API response
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data"`
Error *string `json:"error"`
Meta struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
} `json:"meta"`
}
func handleAPIResponse(responseBody []byte) {
node := fxjson.FromBytes(responseBody)
// Quick success check
if !node.Get("success").BoolOr(false) {
errorMsg := node.Get("error").StringOr("Unknown error")
log.Printf("API Error: %s", errorMsg)
return
}
// Process data based on type
data := node.Get("data")
if data.IsArray() {
// Handle list response
fmt.Printf("Received %d items\n", data.Len())
data.ArrayForEach(func(i int, item fxjson.Node) bool {
// Process each item
id := item.Get("id").IntOr(0)
name := item.Get("name").StringOr("")
fmt.Printf("Item %d: %s\n", id, name)
return true
})
} else if data.IsObject() {
// Handle single item response
id := data.Get("id").IntOr(0)
fmt.Printf("Single item ID: %d\n", id)
}
// Check pagination
if node.HasKey("meta") {
currentPage := node.GetPath("meta.page").IntOr(1)
totalPages := node.GetPath("meta.total").IntOr(0) /
node.GetPath("meta.per_page").IntOr(10)
fmt.Printf("Page %d of %d\n", currentPage, totalPages)
}
}type AppConfig struct {
Server struct {
Host string
Port int
SSL bool
}
Database struct {
Driver string
Host string
Port int
Name string
User string
Password string
Pool int
}
Features map[string]bool
}
func loadConfig(env string) (*AppConfig, error) {
// Load base config
baseConfig, err := os.ReadFile("config/base.json")
if err != nil {
return nil, err
}
// Load environment-specific config
envConfig, err := os.ReadFile(fmt.Sprintf("config/%s.json", env))
if err != nil {
// Environment config is optional
envConfig = []byte("{}")
}
// Parse configs
base := fxjson.FromBytes(baseConfig)
envSpecific := fxjson.FromBytes(envConfig)
// Merge configurations
// Environment-specific values override base values
final := base.Merge(envSpecific)
// Validate required fields
requiredFields := []string{
"server.host",
"server.port",
"database.driver",
"database.host",
"database.name",
}
for _, field := range requiredFields {
if !final.HasKey(field) {
return nil, fmt.Errorf("missing required field: %s", field)
}
}
// Decode to struct
var config AppConfig
if err := final.Decode(&config); err != nil {
return nil, err
}
// Apply defaults for optional fields
if config.Database.Pool == 0 {
config.Database.Pool = 10 // Default pool size
}
if config.Server.Port == 0 {
config.Server.Port = 8080 // Default port
}
return &config, nil
}// Migrate old format to new format
func migrateUserData(oldJSON []byte) ([]byte, error) {
old := fxjson.FromBytes(oldJSON)
// Build new structure
newData := map[string]interface{}{
"version": "2.0",
"user": map[string]interface{}{
"id": old.Get("user_id").IntOr(0),
"username": old.Get("user_name").StringOr(""),
"email": old.Get("email_address").StringOr(""),
"profile": map[string]interface{}{
"firstName": old.Get("first_name").StringOr(""),
"lastName": old.Get("last_name").StringOr(""),
"avatar": old.Get("profile_pic").StringOr(""),
"bio": old.Get("about").StringOr(""),
},
"settings": map[string]interface{}{
"notifications": map[string]interface{}{
"email": old.Get("email_notifications").BoolOr(true),
"push": old.Get("push_notifications").BoolOr(false),
},
"privacy": map[string]interface{}{
"profileVisible": old.Get("public_profile").BoolOr(true),
"showEmail": old.Get("show_email").BoolOr(false),
},
},
"metadata": map[string]interface{}{
"createdAt": old.Get("created_date").StringOr(""),
"modifiedAt": old.Get("modified_date").StringOr(""),
"lastLogin": old.Get("last_login").StringOr(""),
},
},
}
// Handle arrays differently
if old.HasKey("tags") {
tags := old.Get("tags")
if tags.IsString() {
// Old format: comma-separated string
tagStr := tags.StringOr("")
newData["user"].(map[string]interface{})["tags"] = strings.Split(tagStr, ",")
} else if tags.IsArray() {
// Already in new format
if tagSlice, err := tags.ToStringSlice(); err == nil {
newData["user"].(map[string]interface{})["tags"] = tagSlice
}
}
}
// Convert back to JSON
return json.Marshal(newData)
}// Analyze JSON logs
func analyzeJSONLogs(logData []byte) {
// Assume each line is a JSON object
lines := strings.Split(string(logData), "\n")
var (
errorCount int
warnCount int
infoCount int
totalLatency float64
requests int
)
errorMessages := make(map[string]int)
for _, line := range lines {
if line == "" {
continue
}
log := fxjson.FromBytes([]byte(line))
// Count log levels
level := log.Get("level").StringOr("")
switch level {
case "ERROR":
errorCount++
// Track error messages
msg := log.Get("message").StringOr("")
errorMessages[msg]++
case "WARN":
warnCount++
case "INFO":
infoCount++
}
// Analyze performance metrics
if log.HasKey("latency") {
latency := log.Get("latency").FloatOr(0)
totalLatency += latency
requests++
}
// Check for specific patterns
if log.Get("status").IntOr(0) >= 500 {
fmt.Printf("Server error at %s: %s\n",
log.Get("timestamp").StringOr(""),
log.Get("message").StringOr(""))
}
}
// Print summary
fmt.Printf("\n=== Log Analysis Summary ===\n")
fmt.Printf("Total logs: %d\n", len(lines))
fmt.Printf("Errors: %d, Warnings: %d, Info: %d\n",
errorCount, warnCount, infoCount)
if requests > 0 {
avgLatency := totalLatency / float64(requests)
fmt.Printf("Average latency: %.2fms\n", avgLatency)
}
if len(errorMessages) > 0 {
fmt.Printf("\nTop errors:\n")
for msg, count := range errorMessages {
if count > 1 {
fmt.Printf(" - %s (occurred %d times)\n", msg, count)
}
}
}
}// Problem: Empty strings might cause issues
jsonData := []byte(`{"name": "", "description": null}`)
node := fxjson.FromBytes(jsonData)
// Solution: Always use default values
name := node.Get("name").StringOr("Anonymous")
description := node.Get("description").StringOr("No description")
// Check if truly empty
if node.Get("name").IsEmpty() {
fmt.Println("Name is empty")
}// Problem: Floating point precision
jsonData := []byte(`{"price": 19.99, "tax": 1.995}`)
node := fxjson.FromBytes(jsonData)
// Solution 1: Use FloatString() to preserve original format
priceStr, _ := node.Get("price").FloatString() // "19.99"
// Solution 2: Use proper rounding for calculations
price := node.Get("price").FloatOr(0)
tax := node.Get("tax").FloatOr(0)
total := math.Round((price+tax)*100) / 100 // Proper rounding// Problem: JSON within JSON strings
jsonData := []byte(`{
"data": "{\"nested\":\"value\",\"count\":42}"
}`)
// FxJSON automatically expands nested JSON!
node := fxjson.FromBytes(jsonData)
nested := node.Get("data").Get("nested").StringOr("") // Works!
count := node.Get("data").Get("count").IntOr(0) // 42// Problem: Processing large arrays efficiently
largeArray := []byte(`{"items": [/* thousands of items */]}`)
// Solution: Use streaming approach
node := fxjson.FromBytes(largeArray)
items := node.Get("items")
// Process in batches
batchSize := 100
for i := 0; i < items.Len(); i += batchSize {
end := i + batchSize
if end > items.Len() {
end = items.Len()
}
// Process batch
for j := i; j < end; j++ {
item := items.Index(j)
// Process item
}
// Could add sleep here to prevent overwhelming system
// time.Sleep(10 * time.Millisecond)
}// ❌ Don't do this
value, err := node.Get("key").String()
if err != nil {
// Handle error
}
// ✅ Do this instead
value := node.Get("key").StringOr("default")// ❌ Verbose and error-prone
city := node.Get("user").Get("address").Get("city").StringOr("")
// ✅ Clean and readable
city := node.GetPath("user.address.city").StringOr("")// ✅ Safe approach
field := node.Get("unknown_field")
if field.IsNumber() {
value := field.IntOr(0)
// Use value
} else if field.IsString() {
value := field.StringOr("")
// Use value
}// ✅ Always validate structure
requiredFields := []string{"id", "name", "email"}
if !node.HasAllPaths(requiredFields...) {
return errors.New("missing required fields")
}
// Then process safely
id := node.Get("id").IntOr(0)
name := node.Get("name").StringOr("")
email := node.Get("email").StringOr("")// ❌ Slow for large objects/arrays
for i := 0; i < array.Len(); i++ {
item := array.Index(i)
// Process
}
// ✅ Fast, zero-allocation
array.ArrayForEach(func(i int, item fxjson.Node) bool {
// Process
return true
})FxJSON provides powerful serialization capabilities to efficiently convert Go structs, slices, maps, and other data types to JSON.
package main
import (
"fmt"
"github.com/icloudza/fxjson"
)
// Define user struct
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
IsActive bool `json:"is_active"`
Height float64 `json:"height"`
Tags []string `json:"tags,omitempty"`
}
func main() {
user := User{
ID: 123,
Name: "John Doe",
Email: "john@example.com",
IsActive: true,
Height: 175.5,
Tags: []string{"developer", "golang"},
}
// Basic serialization
jsonBytes, err := fxjson.Marshal(user)
if err != nil {
panic(err)
}
fmt.Println("Compact JSON:", string(jsonBytes))
// Pretty print serialization (with indentation)
prettyJSON, err := fxjson.MarshalIndent(user, "", " ")
if err != nil {
panic(err)
}
fmt.Println("Pretty JSON:\n", string(prettyJSON))
// Direct serialization to string
jsonStr, err := fxjson.MarshalToString(user)
if err != nil {
panic(err)
}
fmt.Println("JSON string:", jsonStr)
}Output:
Compact JSON: {"id":123,"name":"John Doe","email":"john@example.com","is_active":true,"height":175.5,"tags":["developer","golang"]}
Pretty JSON:
{
"id": 123,
"name": "John Doe",
"email": "john@example.com",
"is_active": true,
"height": 175.5,
"tags": [
"developer",
"golang"
]
}// Custom serialization options
opts := fxjson.SerializeOptions{
Indent: " ", // 4-space indentation
EscapeHTML: true, // Escape HTML characters
SortKeys: true, // Sort keys
OmitEmpty: true, // Omit empty values
FloatPrecision: 2, // Float precision to 2 decimal places
UseNumberString: false, // Don't use string for large numbers
}
// Serialize with custom options
customJSON, err := fxjson.MarshalWithOptions(user, opts)
if err != nil {
panic(err)
}
// Preset options
// Default options (compact)
defaultJSON, _ := fxjson.MarshalWithOptions(user, fxjson.DefaultSerializeOptions)
// Pretty options (formatted)
prettyJSON, _ := fxjson.MarshalWithOptions(user, fxjson.PrettySerializeOptions)// Ultra-fast serialization (zero allocation, no error handling)
fastJSON := fxjson.FastMarshal(user)
fmt.Println("Fast serialization:", string(fastJSON))
// Struct-specific fast serialization
structJSON, err := fxjson.MarshalStruct(user)
if err != nil {
panic(err)
}
// Ultra-fast struct serialization (no error checking)
fastStructJSON := fxjson.MarshalStructFast(user)// Complex nested structure
type Company struct {
ID int64 `json:"id"`
Name string `json:"name"`
Employees []User `json:"employees"`
Founded time.Time `json:"founded"`
IsPublic bool `json:"is_public"`
Revenue float64 `json:"revenue,omitempty"`
Departments []string `json:"departments,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
IsActive bool `json:"is_active"`
Address *Address `json:"address,omitempty"`
Tags []string `json:"tags,omitempty"`
}
type Address struct {
Street string `json:"street"`
City string `json:"city"`
Country string `json:"country"`
ZipCode string `json:"zip_code,omitempty"`
}
func main() {
company := Company{
ID: 1001,
Name: "Tech Corp",
Employees: []User{
{
ID: 1,
Name: "John Doe",
Email: "john@company.com",
IsActive: true,
Address: &Address{
Street: "123 Main St",
City: "New York",
Country: "USA",
ZipCode: "10001",
},
Tags: []string{"backend", "go-expert"},
},
{
ID: 2,
Name: "Jane Smith",
Email: "jane@company.com",
IsActive: true,
Tags: []string{"frontend", "react-expert"},
},
},
Founded: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
IsPublic: false,
Revenue: 5000000.50,
Departments: []string{"Engineering", "Marketing", "HR"},
Metadata: map[string]interface{}{
"industry": "Software",
"size": "Medium",
"locations": []string{"New York", "San Francisco", "Austin"},
},
}
// Serialize complex structure
companyJSON, err := fxjson.MarshalIndent(company, "", " ")
if err != nil {
panic(err)
}
fmt.Println("Company JSON:")
fmt.Println(string(companyJSON))
}// Batch serialize multiple objects
users := []User{
{ID: 1, Name: "John", Email: "john@example.com"},
{ID: 2, Name: "Jane", Email: "jane@example.com"},
{ID: 3, Name: "Bob", Email: "bob@example.com"},
}
// Method 1: Direct slice serialization
usersJSON, err := fxjson.Marshal(users)
if err != nil {
panic(err)
}
// Method 2: Batch serialize to multiple JSON byte arrays
jsonArrays, err := fxjson.BatchMarshalStructs([]interface{}{
users[0], users[1], users[2],
})
if err != nil {
panic(err)
}
for i, jsonBytes := range jsonArrays {
fmt.Printf("User %d: %s\n", i+1, string(jsonBytes))
}
// Method 3: Concurrent batch serialization (for large datasets)
workerCount := 4 // Use 4 worker goroutines
concurrentJSON, err := fxjson.BatchMarshalStructsConcurrent(
[]interface{}{users[0], users[1], users[2]},
workerCount,
)import (
"bytes"
"github.com/icloudza/fxjson"
)
// Stream serialize large data
func streamMarshalExample() {
var buffer bytes.Buffer
// Create stream marshaler
marshaler := fxjson.NewStreamMarshaler(
func(data []byte) error {
_, err := buffer.Write(data)
return err
},
fxjson.PrettySerializeOptions,
)
// Start writing array
marshaler.StartArray()
// Write users one by one
users := []User{
{ID: 1, Name: "John", Email: "john@example.com"},
{ID: 2, Name: "Jane", Email: "jane@example.com"},
{ID: 3, Name: "Bob", Email: "bob@example.com"},
}
for _, user := range users {
err := marshaler.WriteValue(user)
if err != nil {
panic(err)
}
}
// End array
marshaler.EndArray()
marshaler.Close()
fmt.Println("Stream serialization result:")
fmt.Println(buffer.String())
}
// Stream serialize object
func streamObjectExample() {
var buffer bytes.Buffer
marshaler := fxjson.NewStreamMarshaler(
func(data []byte) error {
_, err := buffer.Write(data)
return err
},
fxjson.DefaultSerializeOptions,
)
// Start writing object
marshaler.StartObject()
// Write fields one by one
marshaler.WriteField("company", "Tech Corp")
marshaler.WriteField("employees", users)
marshaler.WriteField("founded", time.Now())
// End object
marshaler.EndObject()
marshaler.Close()
fmt.Println("Stream object result:")
fmt.Println(buffer.String())
}import "time"
// Time serialization
now := time.Now()
// Standard time format
timeJSON := fxjson.MarshalTime(now)
fmt.Println("Time JSON:", string(timeJSON))
// Unix timestamp
unixJSON := fxjson.MarshalTimeUnix(now)
fmt.Println("Unix timestamp:", string(unixJSON))
// Duration
duration := 5 * time.Minute
durationJSON := fxjson.MarshalDuration(duration)
fmt.Println("Duration:", string(durationJSON))
// Binary data (Base64 encoding)
binaryData := []byte("Hello, World!")
binaryJSON := fxjson.MarshalBinary(binaryData)
fmt.Println("Binary data:", string(binaryJSON))// Performance comparison example
type BenchmarkData struct {
ID int64 `json:"id"`
Name string `json:"name"`
Value string `json:"value"`
}
func performanceComparison() {
data := BenchmarkData{
ID: 12345,
Name: "Test Data",
Value: "This is a test value",
}
// Standard serialization
start := time.Now()
for i := 0; i < 10000; i++ {
_, _ = fxjson.Marshal(data)
}
standardTime := time.Since(start)
// Fast serialization
start = time.Now()
for i := 0; i < 10000; i++ {
_ = fxjson.FastMarshal(data)
}
fastTime := time.Since(start)
// Struct-specific serialization
start = time.Now()
for i := 0; i < 10000; i++ {
_ = fxjson.MarshalStructFast(data)
}
structTime := time.Since(start)
fmt.Printf("Standard serialization: %v\n", standardTime)
fmt.Printf("Fast serialization: %v (%.1fx faster)\n", fastTime,
float64(standardTime)/float64(fastTime))
fmt.Printf("Struct serialization: %v (%.1fx faster)\n", structTime,
float64(standardTime)/float64(structTime))
}// Complete serialization options example
func serializationOptions() {
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Description string `json:"description,omitempty"`
HTML string `json:"html_content"`
Weight float64 `json:"weight"`
Available bool `json:"available"`
}
product := Product{
ID: 1,
Name: "Smartphone",
Price: 999.99,
Description: "", // empty value
HTML: "<p>This is <strong>product</strong> description</p>",
Weight: 0.185,
Available: true,
}
// 1. Compact mode (default)
compact, _ := fxjson.MarshalWithOptions(product, fxjson.SerializeOptions{
Indent: "", // No indentation
EscapeHTML: false, // Don't escape HTML
SortKeys: false, // Don't sort keys
OmitEmpty: false, // Don't omit empty values
})
fmt.Println("Compact mode:", string(compact))
// 2. Pretty mode
pretty, _ := fxjson.MarshalWithOptions(product, fxjson.SerializeOptions{
Indent: " ", // 2-space indentation
EscapeHTML: false, // Don't escape HTML
SortKeys: true, // Sort keys
OmitEmpty: true, // Omit empty values
})
fmt.Println("Pretty mode:\n", string(pretty))
// 3. Safe mode (for web output)
safe, _ := fxjson.MarshalWithOptions(product, fxjson.SerializeOptions{
Indent: " ",
EscapeHTML: true, // Escape HTML characters
SortKeys: true,
OmitEmpty: true,
FloatPrecision: 2, // Keep 2 decimal places for floats
UseNumberString: false,
})
fmt.Println("Safe mode:\n", string(safe))
// 4. Precise numeric mode
precise, _ := fxjson.MarshalWithOptions(product, fxjson.SerializeOptions{
FloatPrecision: 4, // 4 decimal places for floats
UseNumberString: true, // Use string for large numbers
})
fmt.Println("Precise mode:", string(precise))
}// Serialization error handling
func marshalWithErrorHandling() {
type ProblematicStruct struct {
Channel chan int `json:"channel"` // Cannot serialize
Func func() `json:"func"` // Cannot serialize
Valid string `json:"valid"`
}
data := ProblematicStruct{
Channel: make(chan int),
Func: func() {},
Valid: "This field can be serialized",
}
// Try serialization
result, err := fxjson.Marshal(data)
if err != nil {
fmt.Printf("Serialization error: %v\n", err)
// Use FastMarshal as fallback (ignores errors)
safeResult := fxjson.FastMarshal(struct {
Valid string `json:"valid"`
}{
Valid: data.Valid,
})
fmt.Printf("Safe serialization result: %s\n", string(safeResult))
} else {
fmt.Printf("Serialization successful: %s\n", string(result))
}
}
// Debug serialization process
func debugMarshaling() {
user := User{
ID: 123,
Name: "Debug User",
}
// Enable debug mode
fxjson.EnableDebugMode()
defer fxjson.DisableDebugMode()
// Serialize and observe debug info
result, err := fxjson.Marshal(user)
if err != nil {
panic(err)
}
fmt.Printf("Debug serialization result: %s\n", string(result))
}// ✅ Choose the right serialization method
// Normal scenarios - use Marshal
data, err := fxjson.Marshal(obj)
// Performance-sensitive scenarios - use FastMarshal
data := fxjson.FastMarshal(obj)
// Large datasets - use batch or stream serialization
results, err := fxjson.BatchMarshalStructs(objects)
// ✅ Use appropriate options
opts := fxjson.SerializeOptions{
Indent: " ", // Pretty print for development
OmitEmpty: true, // Reduce output size
SortKeys: true, // Ensure consistency
EscapeHTML: true, // Web safety
}
// ✅ Cache frequently used structures
type CachedStruct struct {
cachedJSON []byte
dirty bool
}
func (c *CachedStruct) MarshalJSON() ([]byte, error) {
if c.dirty || c.cachedJSON == nil {
var err error
c.cachedJSON, err = fxjson.Marshal(c)
if err != nil {
return nil, err
}
c.dirty = false
}
return c.cachedJSON, nil
}