Skip to content
Back to Learn
·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 TypeJavaScriptPythonJava (Jackson)GoRust (serde_json)
stringstringstrStringstringString
numbernumberint/floatint/doublefloat64f64/i64
booleanbooleanboolbooleanboolbool
nullnullNonenullnilNone
arrayArraylistArray[]interface{}Vec
objectObjectdictMapmap[string]interface{}Map<String, Value>

Performance Comparison (100K iterations)

LanguageEncode (ms)Decode (ms)LibraryBundle Size
JavaScript (Node 20)1822Built-in0 KB
Python 3.126572stdlib0 KB
Java 21 (Jackson)1215jackson-databind~1.5 MB
Go 1.22810encoding/json0 KB
Rust (serde_json)34serde_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 nullundefined is dropped during encoding. Use null explicitly
  • Empty collections[] vs {} vs null — 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.