-
Notifications
You must be signed in to change notification settings - Fork 0
FxJSON use guidelines (English)
- Quick Start
- Core Concepts
- Basic Operations
- Advanced Features
- JSON Serialization
- Performance Tips
- Real-world Examples
- Best Practices
- Troubleshooting
FxJSON is a high-performance JSON parsing library for Go that makes JSON processing fast, safe, and easy. Think of it as the standard JSON library, but supercharged!
Before (Standard library):
// Lots of error handling, memory allocations
var data map[string]interface{}
if err := json.Unmarshal(jsonBytes, &data); err != nil {
// Handle error
}
name, ok := data["name"].(string)
if !ok {
name = "Unknown"
}After (FxJSON):
// Simple, fast, safe
node := fxjson.FromBytes(jsonBytes)
name := node.Get("name").StringOr("Unknown") // One line, no errors!- 67x faster array processing
- 20x faster object traversal
- Zero memory allocations for basic operations
- Built-in safety with default values
go get github.com/icloudza/fxjsonpackage main
import (
"fmt"
"github.com/icloudza/fxjson"
)
func main() {
// Step 1: Parse JSON from bytes
jsonData := []byte(`{
"name": "Alice",
"age": 30,
"active": true
}`)
// Step 2: Create a node from JSON
node := fxjson.FromBytes(jsonData)
// Step 3: Extract values safely with defaults
name := node.Get("name").StringOr("Unknown")
age := node.Get("age").IntOr(0)
active := node.Get("active").BoolOr(false)
fmt.Printf("%s is %d years old (active: %v)\n", name, age, active)
// Output: Alice is 30 years old (active: true)
}-
No error handling needed -
StringOr(),IntOr(),BoolOr()provide safe defaults - Type safety - Automatic conversion with fallbacks
- Clean code - Simple, readable API
- High performance - Faster than standard library
A Node is FxJSON's main type - think of it as a smart wrapper around any JSON value. Whether you're dealing with an object, array, string, number, boolean, or null, it's all represented as a Node.
jsonData := []byte(`{
"name": "Alice",
"age": 30,
"hobbies": ["reading", "coding"],
"address": {"city": "New York"}
}`)
root := fxjson.FromBytes(jsonData)
// Every field access returns a Node
nameNode := root.Get("name") // Node containing "Alice"
ageNode := root.Get("age") // Node containing 30
hobbiesNode := root.Get("hobbies") // Node containing ["reading", "coding"]The Problem with Traditional JSON:
// Traditional way - lots of error checking
var data map[string]interface{}
json.Unmarshal(jsonBytes, &data)
name, ok := data["name"].(string)
if !ok {
name = "default"
}The FxJSON Solution:
// FxJSON way - simple and safe
node := fxjson.FromBytes(jsonBytes)
name := node.Get("name").StringOr("default") // One line, no errors!For nested JSON, use dot notation paths:
// Instead of chaining multiple Get() calls
city := root.Get("address").Get("city").StringOr("")
// Use paths for cleaner code
city := root.GetPath("address.city").StringOr("")
// Works with arrays too
jsonData := []byte(`{"users": [{"name": "Alice"}, {"name": "Bob"}]}`)
node := fxjson.FromBytes(jsonData)
secondUser := node.GetPath("users[1].name").StringOr("") // "Bob"JSON objects are like Go maps - collections of key-value pairs:
// Example user data
userJSON := []byte(`{
"id": 12345,
"name": "Alice",
"email": "alice@example.com",
"active": true,
"profile": {
"city": "New York",
"age": 28
}
}`)
user := fxjson.FromBytes(userJSON)
// Access basic fields
id := user.Get("id").IntOr(0)
name := user.Get("name").StringOr("Unknown")
email := user.Get("email").StringOr("")
active := user.Get("active").BoolOr(false)
// Access nested fields (two ways)
city1 := user.Get("profile").Get("city").StringOr("") // Method 1
city2 := user.GetPath("profile.city").StringOr("") // Method 2 (cleaner)
// Check if a field exists
if user.HasKey("email") {
fmt.Println("User has an email address")
}
// Get all keys from an object
keys := user.GetAllKeys()
fmt.Println("User fields:", keys) // [id, name, email, active, profile]JSON arrays are ordered lists of values:
// Example shopping cart
cartJSON := []byte(`{
"items": [
{"name": "Laptop", "price": 999},
{"name": "Mouse", "price": 29},
{"name": "Keyboard", "price": 79}
],
"tags": ["electronics", "computers"]
}`)
cart := fxjson.FromBytes(cartJSON)
items := cart.Get("items")
// Get array length
count := items.Len()
fmt.Printf("Cart has %d items\n", count) // Cart has 3 items
// Access array elements by index
firstItem := items.Index(0) // Get first item
firstItemName := firstItem.Get("name").StringOr("")
firstItemPrice := firstItem.Get("price").IntOr(0)
// Iterate through array (high-performance way)
var total int
items.ArrayForEach(func(index int, item fxjson.Node) bool {
name := item.Get("name").StringOr("")
price := item.Get("price").IntOr(0)
total += price
fmt.Printf("%d. %s: $%d\n", index+1, name, price)
return true // continue to next item
})
fmt.Printf("Total: $%d\n", total)tags := cart.Get("tags")
// Get first and last elements
first := tags.First().StringOr("") // "electronics"
last := tags.Last().StringOr("") // "computers"
// Convert to Go slice for advanced operations
if tagList, err := tags.ToStringSlice(); err == nil {
for i, tag := range tagList {
fmt.Printf("Tag %d: %s\n", i+1, tag)
}
}FxJSON automatically handles type checking and conversion:
// Mixed types in JSON
mixedJSON := []byte(`{
"name": "Alice",
"age": 30,
"height": 5.6,
"active": true,
"skills": ["Go", "JavaScript"],
"address": {"city": "NYC"}
}`)
data := fxjson.FromBytes(mixedJSON)
// Safe conversion with defaults (recommended)
name := data.Get("name").StringOr("Unknown") // String
age := data.Get("age").IntOr(0) // Integer
height := data.Get("height").FloatOr(0.0) // Float
active := data.Get("active").BoolOr(false) // Boolean
skills := data.Get("skills") // Array
address := data.Get("address") // Object
// Check types before processing (optional)
if skills.IsArray() {
fmt.Printf("User has %d skills\n", skills.Len())
}
if address.IsObject() {
city := address.Get("city").StringOr("Unknown")
fmt.Printf("User lives in %s\n", city)
}One of FxJSON's biggest advantages is safe extraction with defaults:
// These will NEVER panic or cause errors
unknownField := data.Get("nonexistent")
safeString := unknownField.StringOr("default") // "default"
safeNumber := unknownField.IntOr(-1) // -1
safeBool := unknownField.BoolOr(false) // false
fmt.Printf("Safe values: %s, %d, %v\n", safeString, safeNumber, safeBool)FxJSON includes common validators to make data validation easy:
// User registration form data
formData := []byte(`{
"email": "user@example.com",
"website": "https://mysite.com",
"phone": "+1-555-0123",
"id": "123e4567-e89b-12d3-a456-426614174000"
}`)
form := fxjson.FromBytes(formData)
// Validate email
if form.Get("email").IsValidEmail() {
fmt.Println("✅ Email is valid")
}
// Validate URL
if form.Get("website").IsValidURL() {
fmt.Println("✅ Website URL is valid")
}
// Validate phone number
if form.Get("phone").IsValidPhone() {
fmt.Println("✅ Phone number is valid")
}
// Validate UUID
if form.Get("id").IsValidUUID() {
fmt.Println("✅ ID is a valid UUID")
}userData := []byte(`{
"name": " John Doe ",
"bio": "Software Developer",
"website": "https://johndoe.dev"
}`)
user := fxjson.FromBytes(userData)
name := user.Get("name")
// Built-in string operations
trimmed, _ := name.Trim() // "John Doe" (no spaces)
upper, _ := name.ToUpper() // " JOHN DOE "
lower, _ := name.ToLower() // " john doe "
website := user.Get("website")
// String checking
if website.StartsWith("https://") {
fmt.Println("✅ Secure website")
}
if website.Contains("johndoe") {
fmt.Println("✅ Personal website")
}Get multiple values efficiently:
orderJSON := []byte(`{
"id": "ORDER-123",
"customer": {"name": "Alice", "email": "alice@example.com"},
"total": 99.99
}`)
order := fxjson.FromBytes(orderJSON)
// Get multiple values at once
values := order.GetMultiple(
"id",
"customer.name",
"customer.email",
"total",
)
orderId := values[0].StringOr("")
customerName := values[1].StringOr("Unknown")
email := values[2].StringOr("")
total := values[3].FloatOr(0.0)
fmt.Printf("Order %s: %s (%.2f)\n", orderId, customerName, total)
// Check if all required fields exist
required := []string{"id", "customer.name", "total"}
if order.HasAllPaths(required...) {
fmt.Println("✅ All required fields present")
}Find and filter array elements:
productsJSON := []byte(`{
"products": [
{"id": 1, "name": "Laptop", "price": 999, "inStock": true},
{"id": 2, "name": "Mouse", "price": 29, "inStock": false},
{"id": 3, "name": "Keyboard", "price": 79, "inStock": true}
]
}`)
catalog := fxjson.FromBytes(productsJSON)
products := catalog.Get("products")
// Find first product in stock
_, firstInStock, found := products.FindInArray(func(idx int, product fxjson.Node) bool {
return product.Get("inStock").BoolOr(false)
})
if found {
name := firstInStock.Get("name").StringOr("")
fmt.Printf("First in-stock item: %s\n", name)
}
// Filter all products under $100
affordable := products.FilterArray(func(idx int, product fxjson.Node) bool {
return product.Get("price").IntOr(0) < 100
})
fmt.Printf("Found %d affordable products\n", len(affordable))Convert Go structs to JSON with high performance:
package main
import (
"fmt"
"github.com/icloudza/fxjson"
)
// Define a user struct
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
Active bool `json:"active"`
Tags []string `json:"tags,omitempty"`
}
func main() {
user := User{
ID: 123,
Name: "Alice",
Email: "alice@example.com",
Active: true,
Tags: []string{"developer", "golang"},
}
// Basic serialization (compact)
jsonBytes, err := fxjson.Marshal(user)
if err != nil {
panic(err)
}
fmt.Println("Compact:", string(jsonBytes))
// Pretty print serialization
prettyJSON, err := fxjson.MarshalIndent(user, "", " ")
if err != nil {
panic(err)
}
fmt.Println("Pretty:\n", string(prettyJSON))
}// Custom serialization options
opts := fxjson.SerializeOptions{
Indent: " ", // 2-space indentation
EscapeHTML: true, // Escape HTML characters
SortKeys: true, // Sort object keys
OmitEmpty: true, // Skip empty fields
FloatPrecision: 2, // 2 decimal places for floats
}
customJSON, err := fxjson.MarshalWithOptions(user, opts)
if err != nil {
panic(err)
}
// Or use presets
prettyJSON, _ := fxjson.MarshalWithOptions(user, fxjson.PrettySerializeOptions)
compactJSON, _ := fxjson.MarshalWithOptions(user, fxjson.DefaultSerializeOptions)// Ultra-fast serialization (no error checking)
fastJSON := fxjson.FastMarshal(user)
fmt.Println("Fast:", string(fastJSON))
// Batch serialize multiple objects
users := []User{user1, user2, user3}
batchJSON, err := fxjson.BatchMarshalStructs([]interface{}{user1, user2, user3})// ❌ Slow for large arrays (creates many intermediate objects)
for i := 0; i < array.Len(); i++ {
item := array.Index(i)
// Process item
}
// ✅ Fast, zero-allocation traversal
array.ArrayForEach(func(index int, item fxjson.Node) bool {
// Process item
return true // continue iteration
})// ❌ Traditional way with error handling
value, err := node.Get("key").String()
if err != nil {
value = "default"
}
// ✅ FxJSON way with defaults
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("")// For frequently parsed JSON, use caching
cache := fxjson.NewMemoryCache(100)
fxjson.EnableCaching(cache)
// Parse with cache (subsequent parses will be faster)
node := fxjson.FromBytesWithCache(jsonData, 5*time.Minute)func handleAPIResponse(responseBody []byte) {
response := fxjson.FromBytes(responseBody)
// Check if request was successful
if !response.Get("success").BoolOr(false) {
errorMsg := response.Get("error").StringOr("Unknown error")
log.Printf("API Error: %s", errorMsg)
return
}
// Process data based on type
data := response.Get("data")
if data.IsArray() {
fmt.Printf("Received %d items\n", data.Len())
data.ArrayForEach(func(i int, item fxjson.Node) bool {
id := item.Get("id").IntOr(0)
name := item.Get("name").StringOr("Unknown")
fmt.Printf("Item %d: %s\n", id, name)
return true
})
}
// Handle pagination
currentPage := response.GetPath("meta.page").IntOr(1)
totalPages := response.GetPath("meta.totalPages").IntOr(1)
fmt.Printf("Page %d of %d\n", currentPage, totalPages)
}type Config struct {
Server struct {
Host string `json:"host"`
Port int `json:"port"`
} `json:"server"`
Database struct {
URL string `json:"url"`
PoolSize int `json:"poolSize"`
} `json:"database"`
}
func loadConfig(env string) (*Config, error) {
// Load base config
baseConfig, _ := os.ReadFile("config/base.json")
// Load environment-specific config
envConfig, _ := os.ReadFile(fmt.Sprintf("config/%s.json", env))
if envConfig == nil {
envConfig = []byte("{}")
}
// Parse and merge
base := fxjson.FromBytes(baseConfig)
envSpecific := fxjson.FromBytes(envConfig)
final := base.Merge(envSpecific)
// Validate required fields
required := []string{"server.host", "server.port", "database.url"}
if !final.HasAllPaths(required...) {
return nil, errors.New("missing required configuration")
}
// Decode to struct
var config Config
if err := final.Decode(&config); err != nil {
return nil, err
}
// Apply defaults
if config.Database.PoolSize == 0 {
config.Database.PoolSize = 10
}
return &config, nil
}func analyzeJSONLogs(logData []byte) {
lines := strings.Split(string(logData), "\n")
var errorCount, warnCount, infoCount int
errorMessages := make(map[string]int)
for _, line := range lines {
if line == "" {
continue
}
logEntry := fxjson.FromBytes([]byte(line))
level := logEntry.Get("level").StringOr("")
switch level {
case "ERROR":
errorCount++
msg := logEntry.Get("message").StringOr("")
errorMessages[msg]++
case "WARN":
warnCount++
case "INFO":
infoCount++
}
// Check for server errors
if logEntry.Get("status").IntOr(0) >= 500 {
timestamp := logEntry.Get("timestamp").StringOr("")
message := logEntry.Get("message").StringOr("")
fmt.Printf("Server error at %s: %s\n", timestamp, message)
}
}
fmt.Printf("Log Summary: %d errors, %d warnings, %d info\n",
errorCount, warnCount, infoCount)
// Show frequent errors
for msg, count := range errorMessages {
if count > 1 {
fmt.Printf("Frequent error: %s (%d times)\n", msg, count)
}
}
}// ❌ 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 for unknown data
field := node.Get("unknown_field")
if field.IsNumber() {
value := field.IntOr(0)
} else if field.IsString() {
value := field.StringOr("")
}// ✅ Always validate structure first
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
})// Normal scenarios - use Marshal
data, err := fxjson.Marshal(obj)
// Performance-critical scenarios - use FastMarshal
data := fxjson.FastMarshal(obj)
// Large datasets - use batch serialization
results, err := fxjson.BatchMarshalStructs(objects)// 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: 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: Processing large arrays efficiently
largeArray := []byte(`{"items": [/* thousands of items */]}`)
// Solution: Use ArrayForEach for streaming
node := fxjson.FromBytes(largeArray)
items := node.Get("items")
items.ArrayForEach(func(i int, item fxjson.Node) bool {
// Process item efficiently
return true
})-
Check node types before conversion:
node.IsString(),node.IsArray(), etc. - Use HasKey() to verify field existence before accessing
- Enable caching for frequently parsed JSON to improve performance
-
Use paths (
GetPath()) for deep nested structures
FxJSON makes JSON processing fast, safe, and fun! 🚀
For more examples and advanced usage, check the project repository.