Developer Tools
Jul 7, 202610 min read

Data Serialization Guide: JSON, Base64, QR & Case Rules

Noman Maken
Data Serialization Guide: JSON, Base64, QR & Case Rules

Every API call, every config file, every QR-linked payment link ultimately reduces to the same problem: turning structured data into a sequence of bytes, and turning that sequence back into structured data without loss. This guide walks through the four encoding and serialization layers engineers touch daily JSON payload structure, Base64 byte-array transport, string case conventions for identifiers, and QR code data modes with the underlying computational and byte-layout mechanics, not just the syntax.

The Serialization Payload: JSON as the Universal Data Interchange Format

JSON (JavaScript Object Notation) is not a data type system it's a serialization payload format layered on top of six primitive JavaScript types: string, number, boolean, null, object, and array. A JSON parser builds an abstract syntax tree (AST) from a linear character stream, and every deserialization failure you've ever debugged traces back to a violation of that grammar.

Structural Validation and the AST Parsing Model

A conformant JSON parser is a recursive-descent parser: it tokenizes the input stream (braces, brackets, colons, commas, string literals, numeric literals) and then recursively builds nested value nodes. Parsing runtime is O(n) in the length of the input, since every character is visited a constant number of times regardless of nesting depth the cost that scales with depth is stack usage, not time, which is why extremely deeply nested payloads can trigger stack overflow before they trigger a timing problem.

{
  "requestId": "a4f1-92cd",
  "payload": {
    "userId": 10432,
    "roles": ["admin", "editor"],
    "verified": true,
    "metadata": null
  }
}

Well-formed as this looks, a huge share of real-world "JSON is broken" tickets come from a small, repeatable set of grammar violations: trailing commas after the last array or object member (valid in JS object literals, invalid in strict JSON), unescaped control characters or double quotes inside string literals, single-quoted strings, and numeric values that overflow IEEE 754 double-precision floating point a 19-digit integer ID silently loses precision the moment it's parsed as a JSON number rather than a string. Before wiring a payload into a production pipeline, running it through a JSON Formatter surfaces exactly which token the parser chokes on, rather than a generic "unexpected end of input" from your application logs.

Common Serialization Failure Modes

  • Trailing commas: ["a", "b",] is rejected by JSON.parse even though many linters tolerate it in source code.
  • Duplicate keys: the JSON grammar doesn't forbid them, but the spec leaves resolution order implementation-defined most parsers keep the last occurrence, silently discarding earlier writes.
  • Precision loss: integers beyond Number.MAX_SAFE_INTEGER (2^53 - 1) round incorrectly; snowflake IDs and 64-bit database keys should be serialized as strings.
  • Byte order marks (BOM): a leading UTF-8 BOM byte sequence causes some strict parsers to reject an otherwise valid payload outright.

Byte Array Encoding: Base64 and the Binary-to-Text Bridge

JSON, HTTP headers, and URLs are text-safe transport mediums they assume a constrained alphanumeric or printable-ASCII character set. Raw binary data (images, encryption keys, protobuf messages) doesn't respect that assumption; a raw byte array can contain any value from 0x00 to 0xFF, including control characters that would break a JSON string or terminate a header early. Base64 exists to bridge that gap by re-encoding arbitrary byte arrays into a restricted, transport-safe alphabet.

The Mathematics of Base64: 3-Byte to 4-Character Blocks

Base64 works because 2^6 = 64. Three raw bytes contain exactly 24 bits, and 24 bits divides evenly into four 6-bit groups each group maps to one character in a 64-symbol alphabet (A–Z, a–z, 0–9, plus two symbol characters). This is why Base64-encoded output is deterministically about 33% larger than the input: every 3 bytes of input becomes 4 bytes of output, a fixed 4:3 expansion ratio regardless of the source content.

Input bytes (3):     01001101 01100001 01101110
Regrouped (4x6):     010011  010110  000101  101110
Base64 index:         19      22       5      46
Output characters:    T       W        F       u
Result:               "TWFu"   (Base64 of "Man")

Padding, URI-Safe Variants, and Alphabet Restrictions

Because input isn't always a multiple of 3 bytes, the encoder pads with the = character: one trailing byte produces two padding characters, two trailing bytes produce one. Standard Base64 also includes + and / in its alphabet, both of which are reserved characters in a URI query string per RFC 3986 embedding standard Base64 directly in a URL without percent-encoding will corrupt the value. The URL-safe variant (RFC 4648 §5) substitutes - and _ instead, and typically omits the trailing padding entirely, since the payload length itself is enough for compliant decoders to infer where padding would have gone. If you're preparing a binary attachment, an API key, or a serialized token for transport, running it through a Base64 Encoder handles the byte-grouping and padding arithmetic correctly, which is easy to get subtly wrong in a hand-rolled implementation particularly around the final incomplete group.

Security note: Base64 is an encoding, not a cipher. It has zero cryptographic properties  no key, no confusion, no diffusion. Storing a password or token "encoded" in Base64 provides no protection against anyone who can read the string; it only guarantees the byte sequence survives transport through text-only channels.

String Manipulation and Case Conventions in API Schemas

Once a payload is structurally valid and byte-safe, the next failure surface is lexical: identifier casing. Every language ecosystem has a dominant convention, and cross-boundary systems a Python backend serving a JavaScript frontend, for instance routinely need deterministic, reversible transformation between them.

camelCase, snake_case, PascalCase, kebab-case: Regex Parsing Rules

Converting between cases is a two-step regex parsing problem: first, tokenize the identifier into logical words regardless of its current casing; second, rejoin those words under the target convention's delimiter and capitalization rule.

// Tokenizing step: split on case boundaries, underscores, hyphens, and spaces
const tokens = "userAccount_ID-number"
  .replace(/([a-z0-9])([A-Z])/g, '$1 $2')   // camel/Pascal boundary
  .replace(/[_-]+/g, ' ')                    // snake/kebab delimiters
  .trim()
  .split(/\s+/)
  .map(w => w.toLowerCase());
// tokens => ["user", "account", "id", "number"]
 
// Rejoining step for each target convention
tokens.join('_');                                    // snake_case
tokens.map((w,i)=> i===0? w : w[0].toUpperCase()+w.slice(1)).join(''); // camelCase
tokens.join('-');                                    // kebab-case

The subtlety engineers underestimate is acronym handling: a naive regex splits userID into user_i_d instead of the intended user_id, because it treats every uppercase letter as a new word boundary rather than recognizing a contiguous acronym run. A robust converter needs a lookahead rule that groups consecutive uppercase letters together unless followed by a lowercase letter that signals the start of the next word (e.g., HTTPServerhttp_server, not h_t_t_p_server). Rather than maintaining this edge-case-prone regex in every service, batch-converting field names and identifiers through a Text Case Converter keeps the transformation consistent across an entire schema migration.

Alphanumeric Sanitization for Identifiers and Slugs

A related but distinct problem is sanitizing free-text input into a safe identifier a URL slug, a database column name, or a cache key. This isn't a case-conversion problem; it's a character-class restriction problem, typically expressed as a regex that whitelists a known-safe alphanumeric set: /^[a-z0-9-]+$/ for a URI-safe slug, or /^[a-zA-Z0-9_]+$/ for an SQL-safe identifier. Whitelisting the permitted character class is materially safer than blacklisting disallowed characters, since a blacklist has to anticipate every dangerous input (Unicode homoglyphs, zero-width characters, path traversal sequences), while a whitelist only has to define what's allowed.

QR Codes as a Structured Data Container: Encoding Modes and Error Correction

A QR code isn't a picture of data it's a two-dimensional error-corrected serialization format, governed by ISO/IEC 18004. The choice of encoding mode and error correction level directly determines the byte layout space available and the code's resilience to physical damage or scan noise.

Byte Layout Space: Numeric, Alphanumeric, Byte, and Kanji Modes

The QR spec defines four primary encoding modes, each with a different bits-per-character cost, which is why capacity varies so dramatically depending on your payload's character set:

  • Numeric mode: packs digits three at a time into 10 bits (values 0–999), the most space-efficient mode, used for pure numeric strings like phone numbers.
  • Alphanumeric mode: packs a restricted 45-character set (0–9, A–Z uppercase, and a handful of symbols) two at a time into 11 bits.
  • Byte mode: the general-purpose mode, consuming a full 8 bits per character required for lowercase letters, full URLs, and UTF-8 text, at roughly double the bit cost of alphanumeric mode.
  • Kanji mode: a specialized 13-bit encoding for Shift JIS double-byte characters, more efficient than byte mode for Japanese text specifically.

This is the exact reason a URL encoded as a QR code (mixed-case, byte mode) holds far less data than a purely numeric tracking code of the same physical grid size mode selection is a direct multiplier on usable payload capacity.

Reed-Solomon Error Correction and Capacity Tradeoffs

QR codes use Reed-Solomon error correction, the same family of algorithm used in CDs and DVDs, to reconstruct data even when part of the code is obscured or damaged. Four error correction levels trade payload capacity for resilience:

Level Recovery Capacity Typical Use Case
L ~7% of codewords Clean digital display, max data density
M ~15% of codewords Default for general print use
Q ~25% of codewords Industrial or outdoor labels
H ~30% of codewords Codes with a logo overlay, high damage risk

Higher error correction reserves more of the grid for redundancy codewords rather than payload data, so a level-H code holds noticeably less usable data than a level-L code at the same version (grid size). When you're generating a code to embed a JSON-serialized payload, a signed deep link, or a Base64-encoded token, this tradeoff between correction level and byte capacity determines whether the payload fits at all a QR Code Generator will typically auto-select the minimum version that fits your data at the requested error correction level, so it's worth testing your actual payload length rather than assuming capacity from a generic spec table.

Putting It Together: A Reference Pipeline for API Debugging

These four layers compose in a predictable order during a typical integration task. Consider debugging a webhook payload that embeds a QR-scannable confirmation link:

  1. Validate the raw webhook body's structure and catch grammar errors with a JSON Formatter before it hits your parser in production.
  2. Decode any Base64-encoded binary fields (signatures, attachments) with a Base64 Encoder to inspect the underlying bytes.
  3. Normalize inconsistent field naming between the upstream payload and your internal schema using a Text Case Converter.
  4. Generate a test QR code from the finalized confirmation URL with a QR Code Generator to confirm the payload length fits your chosen error correction level before shipping it to print or mobile.

Frequently Asked Questions

Does Base64 encoding provide encryption or security?

No. Base64 is a reversible binary-to-text encoding scheme, not a cipher. Anyone can decode a Base64 string with a single function call, so it should never substitute for encryption, hashing, or access control on sensitive payloads.

Why does Base64 output end with one or two equals signs?

The equals sign is padding. Base64 groups input into sets of three bytes; when the final group has only one or two bytes, padding characters fill the remaining output slots so decoders can determine the correct original length.

What is the practical difference between camelCase and snake_case in an API schema?

It's a lexical convention, not a data type distinction. camelCase is idiomatic in JavaScript and most frontend-facing JSON; snake_case is idiomatic in Python, Ruby, and many SQL-backed systems. Mismatches between the two commonly cause silent key lookup failures rather than explicit errors.

How much data can a QR code actually store?

It depends on version, mode, and error correction level. At maximum version with numeric-only data and the lowest correction level, capacity approaches 7,089 characters; mixed-case byte-mode text with high error correction typically holds well under 2,000 characters.

Keep Reading