·5 min read
JSON Encoding and Decoding: Base64, Hex, and Unicode Guide
JSON encoding and decoding (also called serialization and deserialization) is the process of converting data between JSON text and in-memory data structures. Every programming language has its own JSON API with unique behaviors, edge cases, and pitfalls. This guide covers JSON encoding and decoding across JavaScript, Python, Java, Go, and Rust with examples, tables, and performance considerations. Use our JSON Formatter to visualize encoded JSON and JSON Validator to verify decoded outputs.
What is Encoding and Decoding?
- Encoding (Serialization) — Converting an in-memory object/struct/value into a JSON string. Used when sending data to an API, writing to a file, or transmitting over a network.
- Decoding (Deserialization) — Converting a JSON string back into an in-memory object/struct/value. Used when parsing API responses, reading configuration files, or processing incoming data.
JavaScript: JSON.stringify() and JSON.parse()
// Encoding
const obj = { name: "Alice", age: 30, active: true, score: null };
const encoded = JSON.stringify(obj);
// '{"name":"Alice","age":30,"active":true,"score":null}'
// Pretty print
JSON.stringify(obj, null, 2);
// With replacer function
JSON.stringify(obj, (key, value) => {
if (typeof value === 'undefined') return null;
return value;
});
// Decoding
const json = '{"name":"Alice","age":30}';
const decoded = JSON.parse(json);
// { name: 'Alice', age: 30 }
// With reviver function
const parsed = JSON.parse(json, (key, value) => {
if (key === 'createdAt') return new Date(value);
return value;
});
Python: json.dumps() and json.loads()
import json
# Encoding
data = {"name": "Alice", "age": 30, "active": True, "score": None}
encoded = json.dumps(data)
# '{"name": "Alice", "age": 30, "active": true, "score": null}'
# Pretty print
json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True)
# Handle non-serializable types
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def person_encoder(obj):
if isinstance(obj, Person):
return {"__type__": "Person", "name": obj.name, "age": obj.age}
raise TypeError
encoded = json.dumps(Person("Alice", 30), default=person_encoder)
# Decoding
json_str = '{"name": "Alice", "age": 30}'
decoded = json.loads(json_str)
# {'name': 'Alice', 'age': 30}
# Custom object hook
def person_decoder(dct):
if dct.get("__type__") == "Person":
return Person(dct["name"], dct["age"])
return dct
decoded = json.loads(json_str, object_hook=person_decoder)
Java: Jackson ObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
ObjectMapper mapper = new ObjectMapper();
// Encoding
Person person = new Person("Alice", 30);
String json = mapper.writeValueAsString(person);
// Pretty print
String pretty = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(person);
// Decoding
Person p = mapper.readValue(json, Person.class);
// Decoding to Map
Map map = mapper.readValue(json, Map.class);
// Configure mapper
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.setSerializationInclusion(Include.NON_NULL);
Go: json.Marshal() and json.Unmarshal()
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string // json:"name"
Age int // json:"age"
Active bool // json:"active"
}
// Encoding
p := Person{Name: "Alice", Age: 30, Active: true}
bytes, err := json.Marshal(p)
// {"name":"Alice","age":30,"active":true}
// Pretty print
bytes, _ = json.MarshalIndent(p, "", " ")
// Decoding
jsonStr := "{"name":"Alice","age":30,"active":true}"
var person Person
err = json.Unmarshal([]byte(jsonString), &person)
// Decoding to map
var result map[string]interface{}
json.Unmarshal([]byte(jsonString), &result)
Type Mapping Across Languages
| JSON Type | JavaScript | Python | Java (Jackson) | Go | Rust (serde_json) |
|---|---|---|---|---|---|
| string | string | str | String | string | String |
| number | number | int/float | int/double | float64 | f64/i64 |
| boolean | boolean | bool | boolean | bool | bool |
| null | null | None | null | nil | None |
| array | Array | list | Array | []interface{} | Vec |
| object | Object | dict | Map | map[string]interface{} | Map<String, Value> |
Performance Comparison (100K iterations)
| Language | Encode (ms) | Decode (ms) | Library | Bundle Size |
|---|---|---|---|---|
| JavaScript (Node 20) | 18 | 22 | Built-in | 0 KB |
| Python 3.12 | 65 | 72 | stdlib | 0 KB |
| Java 21 (Jackson) | 12 | 15 | jackson-databind | ~1.5 MB |
| Go 1.22 | 8 | 10 | encoding/json | 0 KB |
| Rust (serde_json) | 3 | 4 | serde_json | ~200 KB |
Common Pitfalls
- Precision loss — JavaScript loses integer precision beyond 2^53. Use strings for large IDs
- NaN/Infinity — Not valid in JSON. Validate or replace before encoding
- Dates — JSON has no date type. Use ISO 8601 strings consistently
- Cyclic references — Objects that reference themselves cannot be encoded
- Undefined vs null —
undefinedis dropped during encoding. Usenullexplicitly - Empty collections —
[]vs{}vsnull— be consistent in your API design
Next Steps
Test JSON encoding/decoding with our JSON Formatter. Validate your JSON with JSON Validator. Convert between formats with JS Object to JSON. Generate type-safe code with JSON to TypeScript or JSON to Go.