JSON Best Practices: A Complete Guide

General ~3 min read
JSONBest PracticesSecurityPerformanceAPI Design

Comprehensive guide to JSON best practices covering naming conventions, schema design, error handling, performance optimization, and security considerations for production applications.

Naming Conventions

Choose one convention and stick with it:

// Good: camelCase (JavaScript convention, most common)
{
  "firstName": "Alice",
  "lastName": "Smith",
  "createdAt": "2026-07-22T10:00:00Z"
}

// Good: snake_case (Python convention, also common)
{
  "first_name": "Alice",
  "last_name": "Smith",
  "created_at": "2026-07-22T10:00:00Z"
}

// Bad: Avoid mixing conventions
{
  "firstName": "Alice",
  "last_name": "Smith",  // Inconsistent!
  "createdAt": "2026-07-22T10:00:00Z"
}

Recommendation: Use camelCase for JavaScript/TypeScript APIs, snake_case for Python/Ruby APIs. Document your choice.

Data Type Best Practices

Numbers

// Good: Use numbers for numeric values
{ "price": 19.99, "quantity": 42 }

// Bad: Don't use strings for numbers
{ "price": "19.99", "quantity": "42" }

// Good: Use strings for large integers (precision)
{ "id": "9007199254740993" }

// Good: Use null for missing numbers
{ "discount": null }

Dates and Times

// Good: ISO 8601 format (recommended)
{ "createdAt": "2026-07-22T10:30:00Z" }
{ "date": "2026-07-22" }
{ "time": "10:30:00" }

// Bad: Avoid ambiguous formats
{ "date": "07/22/2026" }  // MM/DD/YYYY or DD/MM/YYYY?
{ "timestamp": 1690000000 }  // Unix timestamp - less readable

Booleans

// Good: Use actual booleans
{ "isActive": true, "isDeleted": false }

// Bad: Don't use strings or numbers
{ "isActive": "true" }
{ "isActive": 1 }

Null vs Missing

// Good: Use null for "explicitly empty"
{ "middleName": null }

// Good: Omit field for "not applicable" or "default"
{ "firstName": "Alice", "lastName": "Smith" }
// middleName omitted - not applicable

// Bad: Don't use empty strings for missing values
{ "middleName": "" }  // Is this intentionally empty or missing?

Security Best Practices

Never Trust User Input

// Bad: Dangerous - direct parsing
const data = JSON.parse(userInput);

// Good: Validate schema first
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);

let data;
try {
  data = JSON.parse(userInput);
} catch (e) {
  throw new Error('Invalid JSON input');
}
if (!validate(data)) {
  throw new Error('Invalid data');
}

Prevent Prototype Pollution

// Bad: Vulnerable
const data = JSON.parse(userInput);
// If userInput contains {"__proto__": {"admin": true}}

// Good: Safe parsing with recursive sanitization
function safeParse(json) {
  const data = JSON.parse(json, (key, value) => {
    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
      return undefined;
    }
    return value;
  });
  return data;
}

Size Limits

const MAX_JSON_SIZE = 1024 * 1024; // 1MB

function safeParseWithLimit(json) {
  if (json.length > MAX_JSON_SIZE) {
    throw new Error('JSON too large');
  }
  return JSON.parse(json);
}

Performance Tips

Minimize Payload Size

// Bad: Verbose
{
  "userIdentifier": "12345",
  "userFullName": "Alice Smith",
  "userEmailAddress": "alice@example.com"
}

// Good: Concise (with documentation)
{
  "id": "12345",
  "name": "Alice Smith",
  "email": "alice@example.com"
}

Use Pagination for Large Collections

// Good: Paginated response
{
  "data": [...],
  "pagination": {
    "page": 1,
    "perPage": 20,
    "total": 150,
    "totalPages": 8
  }
}

Compress Large Payloads

// Browsers automatically handle Accept-Encoding headers.
// Server-side compression is what matters:
// Express: app.use(compression())
// Nginx: gzip on;

Common Pitfalls

Trailing Commas

// Bad: Invalid JSON
{ "name": "Alice", "age": 30, }

// Good: Valid JSON
{ "name": "Alice", "age": 30 }

Comments

// Bad: Invalid JSON (use JSONC or JSON5 for comments)
{
  // This is a comment
  "name": "Alice"
}

Single Quotes

// Bad: Invalid JSON
{ 'name': 'Alice' }

// Good: Valid JSON
{ "name": "Alice" }

Key Quoting

// Bad: Invalid JSON
{ name: "Alice" }

// Good: Valid JSON
{ "name": "Alice" }

API Design Patterns

Consistent Envelope

// Good: Success response
{
  "success": true,
  "data": { ... },
  "meta": { "requestId": "abc123" }
}

// Good: Error response
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [...]
  }
}

Sparse Fieldsets

// Request: GET /users/123?fields=name,email
{
  "id": "123",
  "name": "Alice",
  "email": "alice@example.com"
}

Filtering and Sorting

// Request: GET /users?filter[role]=admin&sort=-createdAt
{
  "data": [...],
  "filters": { "role": "admin" },
  "sort": { "field": "createdAt", "order": "desc" }
}

JSON API Comparison Across Languages

Language Library Serialize Deserialize Pretty Print Config
Python json(内置) json.dumps(obj) json.loads(str) json.dumps(obj, indent=2) ensure_ascii, default, cls
JavaScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Java Jackson / Gson mapper.writeValueAsString(obj) mapper.readValue(str, Class.class) mapper.writerWithDefaultPrettyPrinter() 注解、Module、Feature
Go encoding/json(内置) json.Marshal(obj) json.Unmarshal(data, &obj) json.MarshalIndent(obj, "", " ") struct tag
C# System.Text.Json(内置) JsonSerializer.Serialize(obj) JsonSerializer.Deserialize<T>(str) WriteIndented = true JsonSerializerOptions
TypeScript JSON(内置) JSON.stringify(obj) JSON.parse(str) JSON.stringify(obj, null, 2) replacer, space
Rust serde_json serde_json::to_string(&obj) serde_json::from_str::<T>(str) serde_json::to_string_pretty(&obj) serde attributes, custom (de)serializers

Frequently Asked Questions (FAQ)

How to handle datetime types in JSON serialization?

Most language JSON libraries do not support datetime types by default. The common approach is to serialize as ISO 8601 format strings (e.g. "2026-07-15T10:30:00Z") or Unix timestamps, then convert back to datetime objects during deserialization.

How to ignore null values or empty fields in JSON serialization?

Different languages have different implementations: Python can filter None values in custom encoders; JavaScript can use replacer functions; Java Jackson uses @JsonInclude annotations; Go uses omitempty struct tags; C# sets DefaultIgnoreCondition.

How to handle circular references in JSON serialization?

Circular references are a common pitfall. Standard JSON libraries usually throw exceptions or cause stack overflow. Solutions include using @JsonIdentityInfo (Java Jackson), implementing custom serializers, or converting to DTOs before serialization.

Why are my private fields not being serialized?

JSON serialization libraries typically only serialize public fields or properties with getters. This is a security consideration. You can use annotations like @JsonProperty (Java Jackson) or [JsonInclude] (C#) to include non-public members.

What are the performance optimization tips for JSON serialization?

1) Reuse serializer instances; 2) Use streaming APIs for large data; 3) Use compile-time generation (C# Source Generator, Go easyjson); 4) Avoid excessive nesting; 5) Use database pagination for large JSON.

Recommended Tools

These tools can help you handle JSON data more efficiently in General development:

Related Articles