DynamoDB JSON Converter
Convert between DynamoDB's type-wrapped JSON and plain JSON, in whichever direction the input needs.
Conversion payloads stay in this browser tab and are never placed in page URLs - see how local processing works. Privacy-safe analytics record only the tool, outcome, and a coarse input-size bucket. Press Cmd/Ctrl + Enter to convert.
How to convert DynamoDB JSON
Paste either form and the direction is detected from the content. Wrapped documents are unmarshalled into ordinary JSON you can read and diff; ordinary JSON is marshalled into the type-wrapped form the low-level DynamoDB API, the CLI, and CloudFormation seed data expect. Conversion notes list anything that could not round-trip exactly, with the path to each value.
Why DynamoDB JSON looks like that
DynamoDB's wire format annotates every value with its type, so {"n": 42} becomes {"n": {"N": "42"}}. The descriptors are S for string, N for number, B for binary, BOOL for boolean, NULL for null, M for a nested map, L for a heterogeneous list, and SS, NS, and BS for sets of strings, numbers, and binary values. The annotation exists because DynamoDB stores types the JSON type system cannot express — a set that rejects duplicates, and a number with 38 digits of precision. The high-level SDK document clients hide all of this; the low-level API, the AWS CLI, DynamoDB Streams records, and S3 exports do not.
| Input | Output | Note |
|---|---|---|
| {"S": "abc"} | "abc" | String. |
| {"N": "42.5"} | 42.5 | Number, stored as a string for precision. |
| {"BOOL": true} | true | Boolean. |
| {"NULL": true} | null | The value is null; the descriptor is always true. |
| {"L": [{"N":"1"},{"S":"a"}]} | [1, "a"] | List. May mix types. |
| {"M": {"k": {"N":"1"}}} | {"k": 1} | Map. Nests arbitrarily. |
| {"SS": ["a","b"]} | ["a", "b"] | Set. Becomes a List on the way back. |
| {"B": "ZGF0YQ=="} | "ZGF0YQ==" | Binary, kept as base64. |
What the round trip cannot preserve
Three things. Sets lose their set-ness, as described above. An empty string was rejected by DynamoDB for non-key attributes until May 2020, so older tables and some SDK versions still error on one — the converter will happily produce it, and DynamoDB may refuse it. And a number outside the safe integer range cannot become a JavaScript number without changing, so it stays a string and the note tells you which path it was at. Everything else — nesting, ordering within lists, booleans, nulls, and unicode — survives in both directions.
Converting in code
The AWS SDKs ship marshallers, and using them is usually better than a manual walk because they track type-system changes:
JavaScript (AWS SDK v3)
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
const wire = marshall({ id: "order-1042", total: 138.99, paid: true });
const plain = unmarshall(wire);
// removeUndefinedValues is off by default and will throw on undefined:
marshall(item, { removeUndefinedValues: true });Python (boto3)
from boto3.dynamodb.types import TypeDeserializer, TypeSerializer
serializer = TypeSerializer()
deserializer = TypeDeserializer()
wire = {key: serializer.serialize(value) for key, value in item.items()}
plain = {key: deserializer.deserialize(value) for key, value in wire.items()}
# boto3 uses Decimal for N. Convert before json.dumps.AWS CLI
# The CLI speaks the wrapped form directly
aws dynamodb get-item --table-name orders \
--key '{"pk": {"S": "ORDER#1042"}}'
# ...unless you ask for the document form
aws dynamodb scan --table-name orders --output json | jq ".Items"Frequently Asked Questions
Which direction does this convert?
Whichever one your input needs. If the document contains type descriptors such as {"N": "42"} it is unmarshalled to plain JSON; otherwise it is marshalled into DynamoDB's wrapped form. Scan and Query results wrapped in Items, and GetItem results wrapped in Item, are both recognised and unwrapped.
Why are DynamoDB numbers written as strings?
Because DynamoDB's Number type carries up to 38 significant digits, and JSON numbers are IEEE-754 doubles with about 15. Sending 12345678901234567890 as a JSON number would silently change it. Marshalling here always emits N as a string, and unmarshalling keeps a value as a string whenever converting it to a JavaScript number would lose precision, telling you when it does.
What happens to string sets and number sets?
SS, NS, and BS become plain JSON arrays, because JSON has no set type. That is lossy in one specific way: converting back produces a List (L), not a Set, and DynamoDB treats those differently — sets are unordered, reject duplicates, and cannot be empty. The converter says so when it happens; re-declare the attribute as a set by hand if the round trip matters.
How is binary data handled?
The B and BS types hold base64-encoded bytes and are passed through as base64 strings. They are not decoded, because the decoded bytes are frequently not text and would not survive a JSON round trip intact.
Can I convert a whole table export?
Yes for a JSON array of items or a Scan result. A DynamoDB S3 export is JSON Lines with one {"Item": …} object per line — open that with the JSONL viewer first, export it as a JSON array, then convert it here.
Is my data uploaded anywhere?
No. The conversion runs in JavaScript inside the page you already downloaded. There is no upload endpoint on this site, nothing is written into the URL, and closing the tab discards everything. Analytics record the tool name, whether the run succeeded, and a coarse size bucket — never the content.
Related Tools
JSON to JSON Schema
Generate a JSON Schema (draft 2020-12) from example JSON data.
JSON Viewer
Open a large JSON file, search every key and value, filter with JSONPath, and copy any branch — without uploading a byte.
JSON Formatter & Beautifier
Format, beautify, and validate JSON in one pass, with the exact location of any syntax error.