Try the JSON Unescape & Cleaner

Why JSON Gets Double-Encoded — and How to Detect and Fix It

Double-encoded JSON — JSON strings where objects were expected — happens when serialisation runs twice at different layers. Here's how it happens, how to detect it, how to fix it programmatically, and what the common Unicode escape sequences mean.

By sadiqbd · June 9, 2026

Why JSON Gets Double-Encoded — and How to Detect and Fix It

JSON inside JSON is a symptom of architectural decisions that compound over time

The most common cause of "escaped JSON" that needs unescaping isn't a bug — it's a series of reasonable-seeming decisions that produce an unreasonable result. Understanding how it happens makes it easier to prevent, and understanding how to detect and fix it makes debugging faster.


How JSON ends up double-escaped

Scenario 1: Storing serialised objects in database columns

An application needs to store flexible user preferences. Rather than adding many columns, a developer stores JSON in a single VARCHAR or TEXT column:

-- User preferences stored as JSON string in a text column
INSERT INTO users (id, preferences)
VALUES (1, '{"theme": "dark", "language": "en"}');

This is reasonable and common. The problem arises when the serialisation code produces JSON, and then the ORM serialises the entire object to JSON again for transmission:

user = User.query.get(1)
# user.preferences is already a JSON string: '{"theme": "dark", "language": "en"}'

# Bad: serialising the already-serialised string
response = json.dumps({"user": user.to_dict()})
# Result: {"user": {"preferences": "{\"theme\": \"dark\", \"language\": \"en\"}"}}

The preferences field is now a JSON string inside a JSON object. The receiver gets a string where they expected an object and must JSON.parse() it a second time.

The fix: parse the inner JSON when reading from the database, before serialising the outer object:

user_dict = user.to_dict()
user_dict['preferences'] = json.loads(user_dict['preferences'])  # Parse first
response = json.dumps({"user": user_dict})

Scenario 2: Logging JSON data

Debugging code that logs request bodies:

logger.info(f"Request body: {json.dumps(request_body)}")

If request_body is already a string (the raw request body before parsing), this produces a double-escaped log entry. Common in middleware that logs raw HTTP request bodies.

Scenario 3: API wrapping

Service A calls Service B and passes the response to Service C. Service B returns:

{"data": {"status": "ok"}}

Service A's wrapper serialises this for transmission:

wrapped = {"source": "service-b", "payload": json.dumps(service_b_response)}

Service C receives:

{"source": "service-b", "payload": "{\"data\": {\"status\": \"ok\"}}"}

The payload field is a JSON string when it should be a JSON object.


How to detect double-encoded JSON

The string-where-object-expected symptom: You're reading an API response and a field that should be an object or array is a string. In JavaScript:

const data = JSON.parse(response);
typeof data.preferences  // "string" when you expected "object"

The escaped quote tell: Looking at raw JSON and seeing \" inside a string value is a signal:

{"data": "{\"key\": \"value\"}"}

The outer data field contains a JSON string (note the opening {), which needs a second parse.

The parsing test:

try {
  const inner = JSON.parse(data.someField);
  if (typeof inner === 'object') {
    console.log('This field was double-encoded JSON');
  }
} catch (e) {
  // Not JSON, probably a regular string
}

Unicode escaping in JSON

JSON also escapes non-ASCII characters as \uXXXX sequences:

{"name": "\u4e2d\u6587"}

This is valid JSON encoding of the Chinese text "中文". The escape sequences are optional for most Unicode characters (JSON supports UTF-8 directly), but some serialisers produce them for all non-ASCII characters, particularly older Java libraries.

Unescaping these produces the original Unicode characters. The JSON Unescape tool handles this conversion.


Common escaping rules in JSON strings

Inside a JSON string, these sequences must be escaped:

Sequence Meaning
\" Literal double quote
\\ Literal backslash
\/ Literal forward slash (optional, but common)
\n Newline
\r Carriage return
\t Tab
\uXXXX Unicode code point (4 hex digits)
\b Backspace
\f Form feed

The forward slash (/) in JSON strings is optional to escape — most serialisers don't escape it, but some (particularly older PHP implementations) do produce \/. This looks odd in JSON output but is entirely valid.


Fixing double-encoded JSON programmatically

import json

def deep_unescape_json(obj):
    """Recursively parse JSON strings within a JSON structure."""
    if isinstance(obj, str):
        try:
            parsed = json.loads(obj)
            if isinstance(parsed, (dict, list)):
                return deep_unescape_json(parsed)
        except (json.JSONDecodeError, ValueError):
            pass
        return obj
    elif isinstance(obj, dict):
        return {k: deep_unescape_json(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [deep_unescape_json(item) for item in obj]
    return obj
function deepUnescapeJSON(obj) {
    if (typeof obj === 'string') {
        try {
            const parsed = JSON.parse(obj);
            if (typeof parsed === 'object' && parsed !== null) {
                return deepUnescapeJSON(parsed);
            }
        } catch (e) {}
        return obj;
    } else if (Array.isArray(obj)) {
        return obj.map(deepUnescapeJSON);
    } else if (typeof obj === 'object' && obj !== null) {
        return Object.fromEntries(
            Object.entries(obj).map(([k, v]) => [k, deepUnescapeJSON(v)])
        );
    }
    return obj;
}

How to use the JSON Unescape tool on sadiqbd.com

  1. Paste the escaped JSON string — either the raw escaped string or the full JSON object containing escaped values
  2. Unescape — removes backslash escaping, decodes \uXXXX sequences, optionally parses nested JSON strings
  3. View clean output — human-readable, properly formatted JSON

Useful for:

  • Debugging API responses with escaped fields
  • Cleaning up logged JSON data
  • Inspecting JWT payloads that have been serialised as strings
  • Fixing double-encoded database values during data migration

Frequently Asked Questions

What's the difference between JSON escaping and URL encoding? Different encoding systems for different contexts. JSON string escaping uses backslash sequences (\n, \", \uXXXX). URL percent-encoding uses %XX sequences. Both may apply to the same data in different contexts: a URL in a JSON value needs URL-encoding for URL structure AND JSON string escaping for its position in JSON.

Why do some JSON serialisers escape forward slashes? The JSON spec allows but doesn't require escaping / as \/. The common reason: preventing </script> from appearing in JSON embedded in an HTML <script> tag. By escaping the slash, </script> becomes <\/script>, which doesn't close the script element. This is a specific concern when serving JSON directly in HTML pages.

Is the JSON Unescape tool free? Yes — completely free, no sign-up required.


Double-encoded JSON is almost always a serialisation boundary problem. Identifying where the extra encoding was introduced and fixing it at the source produces cleaner code than recursively unescaping at the consumer.

Try the JSON Unescape tool free at sadiqbd.com — remove backslash escaping, decode Unicode sequences, and clean up double-encoded JSON instantly.

Try the related tool:
Open JSON Unescape & Cleaner

More JSON Unescape & Cleaner articles