Einsteiger6 Min. Lesezeit

DynamoDB JSON & Marshalling

Wenn du zum ersten Mal Rohdaten aus der DynamoDB-API liest, sieht das nicht aus wie das JSON, das du hineingegeben hast. Ein schlichtes Objekt wie {"status": "open", "priority": 3} kommt als {"status": {"S": "open"}, "priority": {"N": "3"}} zurück. Jeder Wert steckt in einem Objekt mit genau einem Schlüssel, der seinen Typ benennt. Diese Verpackung ist DynamoDB JSON, und die Umwandlung hin und zurück heißt Marshalling.

Diese Verpackung ist der Weg, auf dem DynamoDB die Typen auf der Leitung eindeutig hält. Sie bringt aber jeden aus dem Tritt, der schlichtes JSON erwartet, und von Hand geschrieben ist sie fehleranfällig.

Was ist DynamoDB JSON?

DynamoDB JSON ist das typmarkierte Wire-Format von DynamoDB, in dem jeder Wert in einem Objekt mit einem einzigen Schlüssel steckt, der seinen Typ benennt — {"S": "open"} für einen String, {"N": "3"} für eine Zahl. Schlichtes JSON dorthin (und zurück) zu wandeln heißt Marshalling. Es hält die Typen eindeutig, denn schlichtes JSON kann weder Sets noch Binary ausdrücken, und weil DynamoDB-Zahlen als Strings über die Leitung gehen, wäre eine untypisierte 3 mehrdeutig.

  • DynamoDB JSON markiert jeden Wert mit seinem Typ{"S": "..."} für einen String, {"N": "..."} für eine Zahl und so weiter.
  • Marshalling = schlichtes JSON → DynamoDB JSON. Unmarshalling = umgekehrt.
  • Zahlen sind auf der Leitung Strings{"N": "3"}, nicht {"N": 3} — um die Präzision zu erhalten.
  • Die Typ-Tags sind das Datentypsystem, mit dem du ohnehin modellierst: S, N, B, BOOL, NULL, L, M, SS, NS, BS.
  • Schreib es nicht von Hand. Der Document Client des SDK (oder ein Konverter) marshallt für dich; von Hand machst du das nur beim Debuggen oder beim Bauen von Expressions.

Das Problem: schlichtes JSON reicht nicht

JSON kennt genau drei Skalar-Arten — String, Zahl, Boolean — plus null, Arrays und Objekte. DynamoDB kennt mehr: Binary und drei Set-Typen (String Set, Number Set, Binary Set), die JSON überhaupt nicht ausdrücken kann. Und weil DynamoDB-Zahlen als Strings über die Leitung gehen, wäre eine untypisierte 3 mehrdeutig — außerdem kann JSON eine Liste nicht von einem Set unterscheiden.

DynamoDB kann dein JSON also nicht einfach so speichern — es braucht den exakten Typ jedes Werts explizit. Der Typ-Deskriptor ist die Art, wie es das verlustfrei tut, bei jedem Request und jeder Response.

Wie die Kodierung funktioniert

Jeder Attributwert wird zu einem Objekt mit genau einem Schlüssel, und dieser Schlüssel ist ein Typ-Deskriptor:

DeskriptorTypBeispiel
SString{"S": "open"}
NZahl (als String){"N": "3"}
BBinary{"B": "dGV4dA=="}
BOOLBoolean{"BOOL": true}
NULLNull{"NULL": true}
LListe{"L": [{"S": "a"}, {"N": "1"}]}
MMap{"M": {"k": {"S": "v"}}}
SS / NS / BSString-, Number-, Binary-Set{"SS": ["a", "b"]}

Listen und Maps verschachteln dieselben Deskriptoren bis ganz nach unten, ein tief strukturiertes Item wird also tief verpackt. Zahlen gehen mit Absicht als Strings über die Leitung — so behält DynamoDB seine vollen 38 Stellen numerischer Präzision, die eine JSON-Zahl (ein IEEE-754-Double, ~15–17 signifikante Stellen) still wegrunden würde. Das sind dieselben Datentypen, mit denen du modellierst; DynamoDB JSON ist nur ihre explizite Form auf der Leitung, definiert in der AWS low-level API reference.

Durchgerechnetes Beispiel: ein Audit-Log-Eintrag

Das schlichte JSON, das du in deiner App schreiben würdest:

{
  "actor": "u-204",
  "action": "ticket.close",
  "ticketId": 8842,
  "tags": ["billing", "urgent"],
  "redacted": false
}

Für die API zu DynamoDB JSON marshallt:

{
  "actor": {"S": "u-204"},
  "action": {"S": "ticket.close"},
  "ticketId": {"N": "8842"},
  "tags": {"SS": ["billing", "urgent"]},
  "redacted": {"BOOL": false}
}

Beachte die Entscheidungen hinter diesem Item: ticketId wurde zu N mit einem String-Wert; tags als String Set (SS) statt als Liste ist eine bewusste Modellierungsentscheidung — ein generischer Konverter, dem du schlichtes JSON gibst, liefert L, denn ein JSON-Array ist geordnet und darf sich wiederholen, während SS dedupliziert und ungeordnet ist. Ob tags ein SS oder ein L sein soll, ist eine Modellierungsfrage, die dir der Konverter nicht abnehmen kann — genau deshalb lohnt es sich, die Kodierung zu verstehen.

Konvertieren in DynoTable

Von Hand lesen oder schreiben musst du das selten. Füg schlichtes JSON in den DynamoDB-JSON-Konverter ein, um es zu marshallen (und zurück), und wenn du einen Request zusammenbaust, gibt der DynamoDB Expression Builder die korrekt marshallte Attributwert-Map gleich neben der Expression aus. In der App selbst zeigt DynoTable Items als schlichte, lesbare Werte und marshallt sie beim Schreiben für dich.

DynoTable zeigt ein Item als schlichte Werte, das rohe DynamoDB JSON bleibt verfügbar.
DynoTable zeigt ein Item als schlichte Werte, das rohe DynamoDB JSON bleibt verfügbar.

Fallstricke + nächste Schritte

  • Zahlen sind in DynamoDB JSON Strings{"N": "3"}. Die Anführungszeichen zählen; gib keine nackte Zahl aus.
  • Set vs. Liste ist eine Modellierungsentscheidung, die die Kodierung sichtbar macht — triff sie bewusst (siehe Datentypen).
  • Nimm im Anwendungscode lieber den Document Client als Marshalling von Hand; heb dir manuelles DynamoDB JSON fürs Debuggen und für Expressions auf.
  • Leere Strings sind für Nicht-Schlüssel-Attribute erlaubt (seit 2020), für Tabellen- und Index-Schlüssel aber weiterhin abgelehnt, und sie haben historisch Tooling zum Stolpern gebracht — prüf die Randfälle.

Du willst Items als schlichte Werte durchsehen, statt Typ-Tags mit dem Auge zu dekodieren? Lade DynoTable herunter und arbeite direkt mit deinen Daten.

Low-Level-Client vs. Document Client

Das AWS SDK bietet zwei Schichten:

SchichtEingabeformWer marshallt
@aws-sdk/client-dynamodb (Low-Level)DynamoDB-JSON-AttributeValue-MapsDein Code oder ein Helfer
@aws-sdk/lib-dynamodb (Document)Schlichte JS-ObjekteDas SDK beim Senden/Empfangen

Anwendungscode sollte für PutItem/GetItem standardmäßig den Document Client nutzen. Zu Low-Level-Maps greifst du, wenn du Update Expressions selbst schreibst oder eine Bibliothek typisierte Attributwerte erwartet.

Expression Attribute Values werden ebenfalls marshallt

Die Platzhalter in ConditionExpression, UpdateExpression und FilterExpression (:val, :inc) verweisen auf marshallte Werte in ExpressionAttributeValues:

":status": {"S": "open"}
":count": {"N": "1"}

Passt das nicht zusammen — etwa "open" ohne den S-Wrapper an den Low-Level-Client —, kommt eine ValidationException zurück. Der Expression Builder gibt die Map neben dem Expression-String aus, damit Platzhalter und Typen im Gleichschritt bleiben.

Attributnamen, die mit reservierten Wörtern kollidieren, laufen stattdessen über ExpressionAttributeNames (#st); das Checker-Tool gibt die Alias-Map fertig zum Einfügen aus.

Marshalling-Überraschungen in Tests

Häufige Testfehler rund ums Marshalling:

  • Leere Sets — DynamoDB lehnt leere SS/NS/BS ab; lass das Attribut stattdessen weg.
  • Fließkommazahlen in N — schick "3.14" als String, nicht als JSON-Zahl, über die Leitung.
  • Binary in NodeUint8Array im Document Client; base64 im rohen JSON.
  • Undefinierte Attribute — der Document Client wirft undefined weg; der Low-Level-Client kann ungültige Payloads schicken.

Wenn ein Lambda rohe API-Responses loggt, füg ein Item in den DynamoDB-JSON-Konverter ein, um lesbares schlichtes JSON zu bekommen, bevor du gegen Fixtures diffst.

Größenwirkung des Taggings

Jeder Typ-Wrapper kostet Bytes. Ein flaches JSON-Objekt, Feld für Feld marshallt, wächst auf der Leitung je nach Attributnamen um grob 30–40 % — diese Aufblähung geht in die Item-Größe und in die RCU-/WCU-Rundung ein. Große Maps mit kurzen Attributnamen amortisieren den Overhead; winzige Boolean-Flags zahlen weiterhin für ihren Schlüsselnamen plus {"BOOL":true}.

Bevor du marshallte Items massenweise lädst, prüf die Gesamtbytes im Item-Size-Rechner, damit ein Batch-Write nicht unerwartet die 16-MB-Request-Grenze reißt.

Die zwei Ansichten in DynoTable

Der Item-Editor hält Marshalling im Alltag unsichtbar — du bearbeitest schlichte Werte, und beim Commit wird beim Senden marshallt. Wenn du ein Produktions-Item debuggst, das du aus CloudWatch-Logs kopiert hast, schaltest du in die DynamoDB-JSON-Ansicht, um die exakten Tags zu sehen, und für Änderungen wieder zurück auf Plain JSON. Export-Aktionen kopieren beide Darstellungen für Tickets und Testfälle.

Aktualisiert